@gentbajko/slopify 3.25.9 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (718) hide show
  1. package/dist/adapters/account-limits.js +30 -0
  2. package/dist/adapters/alignment/audio.js +1 -1
  3. package/dist/adapters/alignment/cache.js +4 -6
  4. package/dist/adapters/alignment/ctc.js +1 -2
  5. package/dist/adapters/alignment/lock.js +4 -3
  6. package/dist/adapters/alignment/multilingual.js +2 -4
  7. package/dist/adapters/alignment/prefetch.js +2 -1
  8. package/dist/adapters/alignment/runner.js +1 -1
  9. package/dist/adapters/alignment/worker.js +3 -2
  10. package/dist/adapters/explain.js +10 -12
  11. package/dist/adapters/ffmpeg.js +19 -1
  12. package/dist/adapters/host-cli/index.js +21 -4
  13. package/dist/adapters/host-cli/transport.js +2 -1
  14. package/dist/adapters/http-failure.js +53 -0
  15. package/dist/adapters/image/bytes.js +2 -1
  16. package/dist/adapters/image/codex.js +42 -9
  17. package/dist/adapters/image/fal.js +21 -53
  18. package/dist/adapters/image/google.js +33 -56
  19. package/dist/adapters/image/models.js +2 -2
  20. package/dist/adapters/image/openai.js +22 -48
  21. package/dist/adapters/image/reference.js +36 -0
  22. package/dist/adapters/image/replicate.js +21 -49
  23. package/dist/adapters/image/video.js +2 -1
  24. package/dist/adapters/interest/search-interest.js +113 -0
  25. package/dist/adapters/key-probes.js +54 -3
  26. package/dist/adapters/llm/antigravity-models.js +1 -1
  27. package/dist/adapters/llm/antigravity.js +11 -6
  28. package/dist/adapters/llm/assistant-cli.js +388 -0
  29. package/dist/adapters/llm/child-session.js +83 -0
  30. package/dist/adapters/llm/claude-code-models.js +52 -99
  31. package/dist/adapters/llm/claude-code.js +16 -4
  32. package/dist/adapters/llm/codex-limits.js +27 -68
  33. package/dist/adapters/llm/codex-models.js +1 -1
  34. package/dist/adapters/llm/codex-server-models.js +61 -109
  35. package/dist/adapters/llm/codex.js +12 -1
  36. package/dist/adapters/llm/document-workspace.js +3 -2
  37. package/dist/adapters/llm/image-workspace.js +3 -2
  38. package/dist/adapters/llm/openrouter.js +12 -48
  39. package/dist/adapters/llm/run-cli.js +9 -24
  40. package/dist/adapters/llm/sse-lines.js +5 -1
  41. package/dist/adapters/model-access.js +37 -0
  42. package/dist/adapters/tts/cartesia.js +15 -46
  43. package/dist/adapters/tts/elevenlabs.js +20 -52
  44. package/dist/adapters/tts/gemini.js +21 -41
  45. package/dist/adapters/tts/inworld-async.js +6 -2
  46. package/dist/adapters/tts/inworld.js +6 -2
  47. package/dist/adapters/tts/openai.js +20 -50
  48. package/dist/adapters/tts/system.js +2 -1
  49. package/dist/assets/models-lifecycle.yaml +6 -0
  50. package/dist/assistant-wiring.js +60 -0
  51. package/dist/autopilot-timers.js +59 -0
  52. package/dist/catalog/lifecycle.js +52 -0
  53. package/dist/catalog/propose.js +204 -0
  54. package/dist/catalog/registry.js +23 -14
  55. package/dist/catalog/schema.js +4 -1
  56. package/dist/catalog/store.js +39 -0
  57. package/dist/catalog/validate.js +5 -2
  58. package/dist/edge/autostart/native.js +3 -5
  59. package/dist/edge/autostart/prompt.js +2 -1
  60. package/dist/edge/autostart/service.js +3 -4
  61. package/dist/edge/cli-args.js +3 -4
  62. package/dist/edge/cli.js +2 -1
  63. package/dist/edge/docker-install/apply.js +11 -11
  64. package/dist/edge/docker-install/run.js +2 -1
  65. package/dist/edge/docker-install/state.js +4 -3
  66. package/dist/edge/docker-install/tree.js +2 -4
  67. package/dist/edge/docker-install/volume.js +3 -2
  68. package/dist/edge/docker.js +5 -4
  69. package/dist/edge/events/hub.js +5 -5
  70. package/dist/edge/http/actions.js +11 -32
  71. package/dist/edge/http/actor.js +5 -3
  72. package/dist/edge/http/app.js +86 -11
  73. package/dist/edge/http/assistant.js +129 -0
  74. package/dist/edge/http/audio-preview.js +4 -11
  75. package/dist/edge/http/auditions.js +1 -1
  76. package/dist/edge/http/autostart.js +5 -5
  77. package/dist/edge/http/backups.js +2 -4
  78. package/dist/edge/http/channel-memory.js +9 -22
  79. package/dist/edge/http/channels.js +14 -27
  80. package/dist/edge/http/checkpoints.js +39 -21
  81. package/dist/edge/http/design-templates.js +167 -0
  82. package/dist/edge/http/designs.js +178 -0
  83. package/dist/edge/http/diagnostics.js +5 -1
  84. package/dist/edge/http/document-themes.js +5 -14
  85. package/dist/edge/http/draft-files.js +1 -3
  86. package/dist/edge/http/draft-problem.js +5 -10
  87. package/dist/edge/http/drafts.js +3 -3
  88. package/dist/edge/http/entries.js +6 -9
  89. package/dist/edge/http/extension-auth.js +27 -0
  90. package/dist/edge/http/external-reviews.js +12 -11
  91. package/dist/edge/http/files.js +5 -11
  92. package/dist/edge/http/folder-location.js +1 -4
  93. package/dist/edge/http/fonts.js +0 -7
  94. package/dist/edge/http/home.js +47 -14
  95. package/dist/edge/http/host-cli.js +18 -8
  96. package/dist/edge/http/ids.js +8 -0
  97. package/dist/edge/http/keywords.js +35 -0
  98. package/dist/edge/http/model-checks.js +132 -0
  99. package/dist/edge/http/narration-peaks.js +5 -13
  100. package/dist/edge/http/onboarding.js +30 -26
  101. package/dist/edge/http/open-folder.js +5 -10
  102. package/dist/edge/http/outputs.js +7 -14
  103. package/dist/edge/http/patch-notes.js +6 -12
  104. package/dist/edge/http/planning.js +3 -7
  105. package/dist/edge/http/problem.js +2 -4
  106. package/dist/edge/http/project-create.js +2 -1
  107. package/dist/edge/http/project-templates.js +31 -19
  108. package/dist/edge/http/projects.js +24 -16
  109. package/dist/edge/http/prompts.js +9 -16
  110. package/dist/edge/http/pronunciations.js +1 -2
  111. package/dist/edge/http/providers.js +48 -34
  112. package/dist/edge/http/refusal.js +29 -0
  113. package/dist/edge/http/remote-access.js +40 -0
  114. package/dist/edge/http/remote-guard.js +71 -0
  115. package/dist/edge/http/request-guard.js +91 -0
  116. package/dist/edge/http/reviews.js +12 -25
  117. package/dist/edge/http/revision-files.js +6 -11
  118. package/dist/edge/http/revisions.js +14 -19
  119. package/dist/edge/http/run-cost.js +1 -2
  120. package/dist/edge/http/same-page.js +80 -0
  121. package/dist/edge/http/schedules.js +69 -40
  122. package/dist/edge/http/settings.js +27 -14
  123. package/dist/edge/http/social.js +283 -0
  124. package/dist/edge/http/staging.js +3 -5
  125. package/dist/edge/http/storage-files.js +5 -7
  126. package/dist/edge/http/storage.js +33 -25
  127. package/dist/edge/http/studio.js +94 -69
  128. package/dist/edge/http/style-preview.js +3 -6
  129. package/dist/edge/http/subtitles.js +3 -17
  130. package/dist/edge/http/template-studio.js +436 -0
  131. package/dist/edge/http/trash.js +11 -12
  132. package/dist/edge/http/tutorial.js +1 -2
  133. package/dist/edge/http/tutorials.js +5 -11
  134. package/dist/edge/http/update.js +7 -13
  135. package/dist/edge/http/waveform.js +3 -6
  136. package/dist/edge/http/youtube-edits.js +3 -4
  137. package/dist/edge/mcp/assistant.js +279 -0
  138. package/dist/edge/mcp/attribution.js +8 -2
  139. package/dist/edge/mcp/call-route.js +11 -2
  140. package/dist/edge/mcp/excluded-routes.js +29 -3
  141. package/dist/edge/mcp/http.js +11 -2
  142. package/dist/edge/mcp/instructions.js +6 -4
  143. package/dist/edge/mcp/library-tools.js +9 -5
  144. package/dist/edge/mcp/remake-tools.js +7 -4
  145. package/dist/edge/mcp/route-tool.js +35 -15
  146. package/dist/edge/mcp/server.js +13 -8
  147. package/dist/edge/mcp/tools-channels.js +11 -9
  148. package/dist/edge/mcp/tools-designs.js +165 -0
  149. package/dist/edge/mcp/tools-library.js +19 -7
  150. package/dist/edge/mcp/tools-planning.js +40 -21
  151. package/dist/edge/mcp/tools-projects.js +38 -11
  152. package/dist/edge/mcp/tools-publishing.js +9 -9
  153. package/dist/edge/mcp/tools-runs.js +23 -10
  154. package/dist/edge/mcp/tools-schedules.js +32 -8
  155. package/dist/edge/mcp/tools-setup.js +75 -11
  156. package/dist/edge/mcp/tools-social.js +47 -0
  157. package/dist/edge/mcp/tools-storage.js +11 -3
  158. package/dist/edge/mcp/tools-template-studio.js +166 -0
  159. package/dist/edge/mcp/tools.js +6 -0
  160. package/dist/edge/remote-access/cli.js +71 -0
  161. package/dist/edge/remote-access/model.js +32 -0
  162. package/dist/edge/remote-access/rules.js +207 -0
  163. package/dist/edge/remote-access/service.js +203 -0
  164. package/dist/edge/remote-access/store.js +44 -0
  165. package/dist/edge/remote-access/views.js +43 -0
  166. package/dist/extension/slopify-studio-chrome.zip +0 -0
  167. package/dist/extension/slopify-studio-firefox.zip +0 -0
  168. package/dist/host-cli/install.js +2 -2
  169. package/dist/host-cli/open-folder.js +2 -2
  170. package/dist/host-cli/paths.js +2 -4
  171. package/dist/host-cli/server.js +4 -4
  172. package/dist/host-cli/service.js +3 -3
  173. package/dist/host-cli/status.js +7 -7
  174. package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
  175. package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
  176. package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
  177. package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
  178. package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
  179. package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
  180. package/dist/kernel/db/migrations/0070-designs.sql +53 -0
  181. package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
  182. package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
  183. package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
  184. package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
  185. package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
  186. package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
  187. package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
  188. package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
  189. package/dist/kernel/db/tx.js +2 -4
  190. package/dist/kernel/errors.js +27 -1
  191. package/dist/kernel/json-answer.js +15 -0
  192. package/dist/kernel/lock.js +4 -9
  193. package/dist/kernel/paths.js +8 -0
  194. package/dist/kernel/pipeline.js +12 -0
  195. package/dist/kernel/ports/assistant-cli.js +4 -0
  196. package/dist/kernel/ports/host-cli.js +3 -0
  197. package/dist/kernel/ports/interest.js +4 -0
  198. package/dist/kernel/ports/languages.js +0 -3
  199. package/dist/kernel/ports/model.js +2 -0
  200. package/dist/kernel/ports/settings-places.js +31 -0
  201. package/dist/kernel/ports/system-speech.js +31 -3
  202. package/dist/kernel/runner/attempt.js +14 -4
  203. package/dist/kernel/runner/audition.js +3 -1
  204. package/dist/kernel/runner/checkpoint-authority.js +2 -1
  205. package/dist/kernel/runner/index.js +8 -10
  206. package/dist/kernel/runner/model-access.js +40 -0
  207. package/dist/kernel/runner/model-check.js +153 -0
  208. package/dist/kernel/runner/providers.js +18 -12
  209. package/dist/kernel/runner/standalone.js +11 -7
  210. package/dist/main.js +338 -21
  211. package/dist/model-catalog.js +20 -0
  212. package/dist/model-checks.js +166 -0
  213. package/dist/patch-notes/4.0.0.md +51 -0
  214. package/dist/patch-notes/4.1.0.md +42 -0
  215. package/dist/patch-notes/index.json +12 -0
  216. package/dist/slices/admission/heads.js +2 -1
  217. package/dist/slices/admission/listing-facts.js +95 -0
  218. package/dist/slices/admission/repo.js +11 -7
  219. package/dist/slices/admission/rules.js +50 -15
  220. package/dist/slices/admission/schema.js +8 -0
  221. package/dist/slices/admission/stage-writes.js +0 -3
  222. package/dist/slices/admission/start.js +48 -31
  223. package/dist/slices/admission/state-words.js +34 -5
  224. package/dist/slices/admission/substitute.js +7 -4
  225. package/dist/slices/agents/attention-posts.js +31 -0
  226. package/dist/slices/agents/attention-templates.js +33 -0
  227. package/dist/slices/agents/attention.js +10 -1
  228. package/dist/slices/agents/connection.js +2 -6
  229. package/dist/slices/agents/model.js +6 -3
  230. package/dist/slices/agents/repo.js +15 -7
  231. package/dist/slices/article/continuation.js +0 -63
  232. package/dist/slices/article/plain.js +3 -2
  233. package/dist/slices/article/segments.js +2 -62
  234. package/dist/slices/assistant/access.js +1 -0
  235. package/dist/slices/assistant/asks.js +80 -0
  236. package/dist/slices/assistant/cost.js +54 -0
  237. package/dist/slices/assistant/gate.js +222 -0
  238. package/dist/slices/assistant/model.js +86 -0
  239. package/dist/slices/assistant/names.js +23 -0
  240. package/dist/slices/assistant/prompt.js +74 -0
  241. package/dist/slices/assistant/repo.js +74 -0
  242. package/dist/slices/assistant/tokens.js +18 -0
  243. package/dist/slices/assistant/turn.js +344 -0
  244. package/dist/slices/autopilot/buffer.js +76 -0
  245. package/dist/slices/autopilot/model.js +14 -0
  246. package/dist/slices/autopilot/notify.js +27 -0
  247. package/dist/slices/autopilot/repo.js +34 -0
  248. package/dist/slices/autopilot/signals.js +194 -0
  249. package/dist/slices/autopilot/warnings.js +85 -0
  250. package/dist/slices/autopilot/weekly.js +100 -0
  251. package/dist/slices/backups/files.js +2 -6
  252. package/dist/slices/backups/repo.js +3 -15
  253. package/dist/slices/backups/service.js +8 -7
  254. package/dist/slices/channels/cast-images.js +3 -2
  255. package/dist/slices/channels/model.js +0 -1
  256. package/dist/slices/channels/rebrand.js +0 -3
  257. package/dist/slices/channels/repo.js +4 -0
  258. package/dist/slices/channels/schema.js +7 -1
  259. package/dist/slices/channels/service.js +10 -0
  260. package/dist/slices/channels/videos.js +3 -15
  261. package/dist/slices/checkpoints/index.js +0 -1
  262. package/dist/slices/control/providers.js +2 -28
  263. package/dist/slices/designs/artwork.js +66 -0
  264. package/dist/slices/designs/budget.js +23 -0
  265. package/dist/slices/designs/calls.js +124 -0
  266. package/dist/slices/designs/check-context.js +38 -0
  267. package/dist/slices/designs/check-text.js +43 -0
  268. package/dist/slices/designs/fields.js +88 -0
  269. package/dist/slices/designs/frames.js +34 -0
  270. package/dist/slices/designs/gate.js +130 -0
  271. package/dist/slices/designs/layout.js +293 -0
  272. package/dist/slices/designs/model.js +35 -0
  273. package/dist/slices/designs/pdf-job.js +23 -0
  274. package/dist/slices/designs/pdf-pages.js +30 -0
  275. package/dist/slices/designs/pdf-runner.js +68 -0
  276. package/dist/slices/designs/pdf-worker.js +24 -0
  277. package/dist/slices/designs/picture-size.js +79 -0
  278. package/dist/slices/designs/pictures.js +55 -0
  279. package/dist/slices/designs/quote.js +24 -0
  280. package/dist/slices/designs/render-args.js +36 -0
  281. package/dist/slices/designs/render-input.js +59 -0
  282. package/dist/slices/designs/render.js +215 -0
  283. package/dist/slices/designs/repo.js +101 -0
  284. package/dist/slices/designs/requests.js +80 -0
  285. package/dist/slices/designs/runner.js +239 -0
  286. package/dist/slices/designs/sample-repo.js +91 -0
  287. package/dist/slices/designs/sample.js +223 -0
  288. package/dist/slices/designs/schema.js +87 -0
  289. package/dist/slices/designs/sections.js +44 -0
  290. package/dist/slices/designs/service.js +233 -0
  291. package/dist/slices/designs/steps-repo.js +102 -0
  292. package/dist/slices/designs/studio-kind.js +39 -0
  293. package/dist/slices/designs/template-checks.js +44 -0
  294. package/dist/slices/designs/template-make.js +118 -0
  295. package/dist/slices/designs/templates.js +218 -0
  296. package/dist/slices/document/fonts.js +2 -1
  297. package/dist/slices/document/model.js +7 -4
  298. package/dist/slices/document/render.js +2 -1
  299. package/dist/slices/document/theme-schema.js +1 -1
  300. package/dist/slices/document/theme.js +49 -3
  301. package/dist/slices/episodes/summarize.js +6 -2
  302. package/dist/slices/estimate/index.js +72 -28
  303. package/dist/slices/estimate/requests.js +4 -4
  304. package/dist/slices/fixes/rules.js +3 -1
  305. package/dist/slices/fonts/catalog.js +5 -3
  306. package/dist/slices/fonts/coverage.js +2 -1
  307. package/dist/slices/fonts/discovery.js +3 -2
  308. package/dist/slices/fonts/files.js +2 -6
  309. package/dist/slices/fonts/index.js +0 -1
  310. package/dist/slices/fonts/sfnt.js +45 -10
  311. package/dist/slices/images/appearance.js +8 -16
  312. package/dist/slices/images/scenes.js +102 -28
  313. package/dist/slices/keywords/digest.js +85 -0
  314. package/dist/slices/keywords/facts.js +92 -0
  315. package/dist/slices/keywords/fill.js +80 -0
  316. package/dist/slices/keywords/model.js +75 -0
  317. package/dist/slices/keywords/rules.js +187 -0
  318. package/dist/slices/keywords/schema.js +28 -0
  319. package/dist/slices/keywords/specs.js +260 -0
  320. package/dist/slices/keywords/values.js +74 -0
  321. package/dist/slices/library/lint.js +29 -12
  322. package/dist/slices/library/model.js +2 -0
  323. package/dist/slices/library/photorealistic.js +4 -12
  324. package/dist/slices/library/rename.js +9 -7
  325. package/dist/slices/library/save.js +14 -10
  326. package/dist/slices/library/slots.js +37 -11
  327. package/dist/slices/library/transfer.js +5 -4
  328. package/dist/slices/library/used-by.js +25 -10
  329. package/dist/slices/loudness/loudnorm.js +53 -12
  330. package/dist/slices/loudness/model.js +3 -2
  331. package/dist/slices/loudness/sentence-level.js +2 -1
  332. package/dist/slices/model-checks/cost.js +61 -0
  333. package/dist/slices/model-checks/listing.js +124 -0
  334. package/dist/slices/model-checks/model.js +20 -0
  335. package/dist/slices/model-checks/record.js +32 -0
  336. package/dist/slices/model-checks/repo.js +120 -0
  337. package/dist/slices/model-checks/rules.js +125 -0
  338. package/dist/slices/model-checks/run.js +143 -0
  339. package/dist/slices/model-checks/service.js +198 -0
  340. package/dist/slices/model-upkeep/model.js +1 -1
  341. package/dist/slices/model-upkeep/switch.js +72 -27
  342. package/dist/slices/model-upkeep/usage.js +39 -13
  343. package/dist/slices/narration/breaths.js +3 -1
  344. package/dist/slices/narration/clean-pieces.js +3 -1
  345. package/dist/slices/narration/concat.js +2 -1
  346. package/dist/slices/narration/pauses-model.js +3 -2
  347. package/dist/slices/narration/peaks.js +3 -4
  348. package/dist/slices/narration/pronunciation-chunks.js +4 -3
  349. package/dist/slices/narration/pronunciation.js +6 -4
  350. package/dist/slices/narration/steering.js +4 -3
  351. package/dist/slices/notifications/notifier.js +4 -3
  352. package/dist/slices/notifications/rules.js +5 -2
  353. package/dist/slices/notifications/settings.js +4 -12
  354. package/dist/slices/notifications/subject.js +4 -3
  355. package/dist/slices/onboarding/copy.js +21 -3
  356. package/dist/slices/onboarding/first-run.js +16 -40
  357. package/dist/slices/onboarding/full-video.js +3 -2
  358. package/dist/slices/onboarding/install.js +6 -4
  359. package/dist/slices/onboarding/model.js +26 -37
  360. package/dist/slices/onboarding/packs.js +22 -22
  361. package/dist/slices/onboarding/preferred-model.js +14 -0
  362. package/dist/slices/onboarding/quick-short.js +105 -65
  363. package/dist/slices/onboarding/sample.js +4 -2
  364. package/dist/slices/onboarding/state.js +9 -11
  365. package/dist/slices/onboarding/template.js +8 -6
  366. package/dist/slices/outputs/add.js +3 -2
  367. package/dist/slices/outputs/service.js +2 -2
  368. package/dist/slices/patch-notes/library.js +4 -3
  369. package/dist/slices/play-drafts/convert.js +4 -0
  370. package/dist/slices/play-drafts/readiness.js +3 -3
  371. package/dist/slices/play-drafts/review-inputs.js +9 -3
  372. package/dist/slices/play-drafts/review.js +5 -4
  373. package/dist/slices/play-drafts/schema.js +4 -0
  374. package/dist/slices/play-drafts/service.js +17 -7
  375. package/dist/slices/project-templates/from-project.js +9 -2
  376. package/dist/slices/project-templates/next-chapter.js +2 -1
  377. package/dist/slices/project-templates/provider-defaults.js +22 -0
  378. package/dist/slices/project-templates/repo.js +6 -1
  379. package/dist/slices/project-templates/setup-sections.js +26 -0
  380. package/dist/slices/project-templates/setup.js +14 -1
  381. package/dist/slices/project-templates/stand-ins.js +22 -0
  382. package/dist/slices/rebuild/activity.js +2 -0
  383. package/dist/slices/rebuild/admission-repo.js +3 -2
  384. package/dist/slices/rebuild/narration-history.js +4 -5
  385. package/dist/slices/rebuild/narration-retry.js +2 -1
  386. package/dist/slices/rebuild/preview-details.js +32 -14
  387. package/dist/slices/rebuild/preview-plan.js +3 -8
  388. package/dist/slices/rebuild/recipe-build.js +8 -3
  389. package/dist/slices/rebuild/recipe-describe.js +3 -3
  390. package/dist/slices/rebuild/recipe-digest.js +58 -0
  391. package/dist/slices/rebuild/recipe-input-schema.js +10 -0
  392. package/dist/slices/rebuild/recipe-keywords.js +71 -0
  393. package/dist/slices/rebuild/recipe-legacy.js +5 -1
  394. package/dist/slices/rebuild/recipe-model.js +4 -0
  395. package/dist/slices/rebuild/recipe-narration-text.js +2 -1
  396. package/dist/slices/rebuild/recipe-preparation.js +4 -4
  397. package/dist/slices/rebuild/recipe-reference.js +8 -4
  398. package/dist/slices/rebuild/recipe-reviews.js +27 -7
  399. package/dist/slices/rebuild/recipe-save.js +8 -5
  400. package/dist/slices/rebuild/recipe-scenes.js +40 -11
  401. package/dist/slices/rebuild/recipe-shorts.js +8 -8
  402. package/dist/slices/rebuild/recipe-social.js +46 -0
  403. package/dist/slices/rebuild/recipe-text.js +64 -20
  404. package/dist/slices/rebuild/recipe-validation.js +102 -31
  405. package/dist/slices/rebuild/recipe-visual.js +55 -16
  406. package/dist/slices/rebuild/recipe-voices.js +3 -2
  407. package/dist/slices/rebuild/recipe-work.js +19 -7
  408. package/dist/slices/rebuild/recipe-youtube.js +4 -3
  409. package/dist/slices/rebuild/recovery-selection.js +3 -1
  410. package/dist/slices/rebuild/recovery.js +9 -8
  411. package/dist/slices/rebuild/remake-outdated.js +5 -5
  412. package/dist/slices/rebuild/repo.js +4 -3
  413. package/dist/slices/rebuild/review-promise.js +25 -0
  414. package/dist/slices/rebuild/review-redo.js +3 -2
  415. package/dist/slices/rebuild/runtime-admission.js +4 -3
  416. package/dist/slices/rebuild/runtime-agent-review.js +11 -5
  417. package/dist/slices/rebuild/runtime-animate.js +10 -9
  418. package/dist/slices/rebuild/runtime-article.js +7 -5
  419. package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
  420. package/dist/slices/rebuild/runtime-document.js +11 -13
  421. package/dist/slices/rebuild/runtime-export-bed.js +8 -12
  422. package/dist/slices/rebuild/runtime-export-edit.js +8 -16
  423. package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
  424. package/dist/slices/rebuild/runtime-export-short.js +9 -5
  425. package/dist/slices/rebuild/runtime-export.js +38 -15
  426. package/dist/slices/rebuild/runtime-figure-card.js +2 -1
  427. package/dist/slices/rebuild/runtime-image.js +8 -12
  428. package/dist/slices/rebuild/runtime-instructions.js +2 -0
  429. package/dist/slices/rebuild/runtime-keywords.js +59 -0
  430. package/dist/slices/rebuild/runtime-lines.js +2 -11
  431. package/dist/slices/rebuild/runtime-local.js +17 -22
  432. package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
  433. package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
  434. package/dist/slices/rebuild/runtime-narration-text.js +13 -17
  435. package/dist/slices/rebuild/runtime-pictures.js +2 -4
  436. package/dist/slices/rebuild/runtime-piece-label.js +10 -6
  437. package/dist/slices/rebuild/runtime-plan.js +31 -5
  438. package/dist/slices/rebuild/runtime-provider.js +67 -25
  439. package/dist/slices/rebuild/runtime-publication.js +10 -9
  440. package/dist/slices/rebuild/runtime-review.js +39 -33
  441. package/dist/slices/rebuild/runtime-run.js +4 -1
  442. package/dist/slices/rebuild/runtime-shorts.js +31 -50
  443. package/dist/slices/rebuild/runtime-store.js +20 -10
  444. package/dist/slices/rebuild/runtime-subtitles.js +28 -25
  445. package/dist/slices/rebuild/runtime-voices.js +10 -16
  446. package/dist/slices/rebuild/runtime-youtube.js +14 -31
  447. package/dist/slices/rebuild/service-readiness.js +11 -3
  448. package/dist/slices/rebuild/transition-repo.js +6 -5
  449. package/dist/slices/rebuild/work-records.js +6 -0
  450. package/dist/slices/reruns/cascade.js +1 -2
  451. package/dist/slices/reruns/index.js +6 -9
  452. package/dist/slices/research/synthesis.js +3 -2
  453. package/dist/slices/reviews/external-model.js +4 -1
  454. package/dist/slices/reviews/external-repo.js +2 -1
  455. package/dist/slices/reviews/external-rows.js +2 -3
  456. package/dist/slices/reviews/holds.js +5 -4
  457. package/dist/slices/reviews/model.js +32 -11
  458. package/dist/slices/reviews/repo.js +2 -1
  459. package/dist/slices/reviews/rules.js +4 -3
  460. package/dist/slices/reviews/verdict.js +2 -1
  461. package/dist/slices/reviews/view.js +5 -2
  462. package/dist/slices/revisions/downloads.js +4 -5
  463. package/dist/slices/revisions/image-plan.js +1 -1
  464. package/dist/slices/revisions/mutation-assets.js +9 -5
  465. package/dist/slices/revisions/mutation-cues.js +2 -5
  466. package/dist/slices/revisions/mutations.js +3 -3
  467. package/dist/slices/revisions/repo.js +3 -1
  468. package/dist/slices/revisions/schema.js +1 -0
  469. package/dist/slices/revisions/view.js +20 -0
  470. package/dist/slices/run-cost/limits.js +5 -2
  471. package/dist/slices/run-cost/meter.js +12 -5
  472. package/dist/slices/run-cost/panel.js +8 -74
  473. package/dist/slices/run-cost/plan-readings.js +127 -0
  474. package/dist/slices/run-cost/plan-use.js +169 -0
  475. package/dist/slices/run-cost/pricing.js +3 -3
  476. package/dist/slices/run-cost/week.js +32 -4
  477. package/dist/slices/schedules/agenda.js +18 -7
  478. package/dist/slices/schedules/by-date.js +42 -0
  479. package/dist/slices/schedules/calendar.js +5 -0
  480. package/dist/slices/schedules/design-agenda.js +36 -0
  481. package/dist/slices/schedules/design-check.js +41 -0
  482. package/dist/slices/schedules/design-run.js +126 -0
  483. package/dist/slices/schedules/next-run.js +62 -0
  484. package/dist/slices/schedules/prepare.js +18 -2
  485. package/dist/slices/schedules/repo.js +30 -21
  486. package/dist/slices/schedules/rescore.js +35 -0
  487. package/dist/slices/schedules/scheduler.js +13 -3
  488. package/dist/slices/schedules/schema.js +57 -3
  489. package/dist/slices/schedules/score.js +107 -0
  490. package/dist/slices/schedules/service.js +57 -24
  491. package/dist/slices/schedules/topic-list.js +15 -0
  492. package/dist/slices/schedules/topic-scores.js +104 -0
  493. package/dist/slices/schedules/topics.js +53 -41
  494. package/dist/slices/settings/cli-paths.js +4 -6
  495. package/dist/slices/settings/first-run.js +8 -43
  496. package/dist/slices/settings/health.js +30 -15
  497. package/dist/slices/settings/key-test.js +1 -1
  498. package/dist/slices/settings/model-reach.js +3 -17
  499. package/dist/slices/settings/model.js +14 -2
  500. package/dist/slices/settings/playback.js +34 -23
  501. package/dist/slices/settings/probe-read.js +16 -0
  502. package/dist/slices/settings/repo.js +33 -0
  503. package/dist/slices/settings/tutorial.js +2 -2
  504. package/dist/slices/settings/voices.js +4 -12
  505. package/dist/slices/shorts/keep.js +2 -1
  506. package/dist/slices/shorts/pick.js +6 -18
  507. package/dist/slices/shorts/prompts.js +4 -14
  508. package/dist/slices/shorts/render.js +6 -2
  509. package/dist/slices/social/channel.js +44 -0
  510. package/dist/slices/social/defaults.js +17 -0
  511. package/dist/slices/social/due.js +36 -0
  512. package/dist/slices/social/extension.js +18 -0
  513. package/dist/slices/social/fit.js +115 -0
  514. package/dist/slices/social/limits.js +186 -0
  515. package/dist/slices/social/links.js +21 -0
  516. package/dist/slices/social/media.js +132 -0
  517. package/dist/slices/social/model.js +75 -0
  518. package/dist/slices/social/notice.js +19 -0
  519. package/dist/slices/social/pack.js +176 -0
  520. package/dist/slices/social/post-prompt.js +56 -0
  521. package/dist/slices/social/queue.js +31 -0
  522. package/dist/slices/social/repo.js +133 -0
  523. package/dist/slices/social/reshape.js +97 -0
  524. package/dist/slices/social/rules.js +5 -0
  525. package/dist/slices/social/schema.js +70 -0
  526. package/dist/slices/social/serve.js +51 -0
  527. package/dist/slices/social/service.js +129 -0
  528. package/dist/slices/social/settings.js +21 -0
  529. package/dist/slices/social/sources.js +114 -0
  530. package/dist/slices/social/state.js +110 -0
  531. package/dist/slices/social/text.js +149 -0
  532. package/dist/slices/storage/arrange.js +3 -1
  533. package/dist/slices/storage/assets.js +8 -10
  534. package/dist/slices/storage/backup-designs.js +74 -0
  535. package/dist/slices/storage/backup-export.js +8 -3
  536. package/dist/slices/storage/backup-format.js +10 -0
  537. package/dist/slices/storage/backup-import.js +22 -4
  538. package/dist/slices/storage/delete-project.js +6 -5
  539. package/dist/slices/storage/files-location.js +6 -8
  540. package/dist/slices/storage/layout.js +13 -2
  541. package/dist/slices/storage/model.js +3 -0
  542. package/dist/slices/storage/portable.js +17 -12
  543. package/dist/slices/storage/schema.js +2 -0
  544. package/dist/slices/storage/staging-refs.js +2 -1
  545. package/dist/slices/storage/staging.js +6 -9
  546. package/dist/slices/storage/trim.js +26 -9
  547. package/dist/slices/studio/calendar.js +7 -2
  548. package/dist/slices/studio/disclosure.js +4 -4
  549. package/dist/slices/studio/model.js +1 -1
  550. package/dist/slices/studio/pack.js +9 -8
  551. package/dist/slices/studio/pick.js +2 -11
  552. package/dist/slices/studio/plan.js +7 -16
  553. package/dist/slices/studio/queue.js +3 -14
  554. package/dist/slices/studio/releases.js +26 -0
  555. package/dist/slices/studio/settings.js +20 -57
  556. package/dist/slices/studio/videos.js +35 -4
  557. package/dist/slices/style-preview/render.js +37 -16
  558. package/dist/slices/style-preview/schema.js +14 -1
  559. package/dist/slices/style-preview/service.js +2 -1
  560. package/dist/slices/style-preview/settings.js +33 -5
  561. package/dist/slices/subtitles/captions.js +1 -1
  562. package/dist/slices/telemetry/collector-client.js +2 -4
  563. package/dist/slices/telemetry/flush.js +2 -4
  564. package/dist/slices/telemetry/model.js +0 -7
  565. package/dist/slices/telemetry/record.js +10 -3
  566. package/dist/slices/template-studio/anatomy-view.js +161 -0
  567. package/dist/slices/template-studio/anatomy.js +73 -0
  568. package/dist/slices/template-studio/bakeoff-make.js +225 -0
  569. package/dist/slices/template-studio/bakeoff-repo.js +83 -0
  570. package/dist/slices/template-studio/bakeoff.js +333 -0
  571. package/dist/slices/template-studio/budget.js +106 -0
  572. package/dist/slices/template-studio/cards.js +78 -0
  573. package/dist/slices/template-studio/checks.js +322 -0
  574. package/dist/slices/template-studio/document-sample.js +56 -0
  575. package/dist/slices/template-studio/edit.js +15 -0
  576. package/dist/slices/template-studio/for-all.js +161 -0
  577. package/dist/slices/template-studio/free-video.js +31 -0
  578. package/dist/slices/template-studio/free.js +176 -0
  579. package/dist/slices/template-studio/gate.js +40 -0
  580. package/dist/slices/template-studio/hidden.js +20 -0
  581. package/dist/slices/template-studio/jobs.js +79 -0
  582. package/dist/slices/template-studio/loudness.js +111 -0
  583. package/dist/slices/template-studio/make.js +40 -0
  584. package/dist/slices/template-studio/model.js +58 -0
  585. package/dist/slices/template-studio/pack-import.js +253 -0
  586. package/dist/slices/template-studio/pack-samples.js +60 -0
  587. package/dist/slices/template-studio/pack.js +207 -0
  588. package/dist/slices/template-studio/paid-view.js +66 -0
  589. package/dist/slices/template-studio/paid.js +355 -0
  590. package/dist/slices/template-studio/quote.js +155 -0
  591. package/dist/slices/template-studio/ready.js +104 -0
  592. package/dist/slices/template-studio/repo.js +134 -0
  593. package/dist/slices/template-studio/reviews-path.js +58 -0
  594. package/dist/slices/template-studio/runner.js +84 -0
  595. package/dist/slices/template-studio/save.js +236 -0
  596. package/dist/slices/template-studio/say-it-as.js +248 -0
  597. package/dist/slices/template-studio/schedule-gate.js +34 -0
  598. package/dist/slices/template-studio/schedule-price.js +32 -0
  599. package/dist/slices/template-studio/schedule-sample.js +91 -0
  600. package/dist/slices/template-studio/schema.js +243 -0
  601. package/dist/slices/template-studio/scope.js +124 -0
  602. package/dist/slices/template-studio/sections.js +176 -0
  603. package/dist/slices/template-studio/service.js +175 -0
  604. package/dist/slices/template-studio/status.js +191 -0
  605. package/dist/slices/template-studio/topic.js +223 -0
  606. package/dist/slices/thumbnail/by-llm.js +1 -1
  607. package/dist/slices/trash/designs.js +76 -0
  608. package/dist/slices/trash/model.js +13 -4
  609. package/dist/slices/trash/service.js +52 -2
  610. package/dist/slices/tutorials/library.js +3 -2
  611. package/dist/slices/uploads/repo.js +37 -1
  612. package/dist/slices/uploads/rules.js +3 -2
  613. package/dist/slices/video/ass-text.js +17 -0
  614. package/dist/slices/video/audio-export.js +2 -64
  615. package/dist/slices/video/edit-list.js +2 -2
  616. package/dist/slices/video/edit-settings.js +1 -1
  617. package/dist/slices/video/encoding.js +169 -0
  618. package/dist/slices/video/ffmpeg.js +23 -21
  619. package/dist/slices/video/figure-card.js +1 -15
  620. package/dist/slices/video/plan.js +2 -1
  621. package/dist/slices/video/slideshow.js +43 -18
  622. package/dist/slices/voices/attribution.js +3 -2
  623. package/dist/slices/voices/model.js +9 -8
  624. package/dist/slices/voices/palette.js +0 -3
  625. package/dist/slices/voices/script.js +2 -1
  626. package/dist/slices/youtube/answer.js +4 -16
  627. package/dist/slices/youtube/edits-repo.js +3 -12
  628. package/dist/tutorials/AI-CLIs.md +25 -25
  629. package/dist/tutorials/Agents-MCP.md +34 -33
  630. package/dist/tutorials/Assistant.md +42 -0
  631. package/dist/tutorials/Backups.md +7 -7
  632. package/dist/tutorials/Calendar.md +17 -25
  633. package/dist/tutorials/Cast-Library.md +19 -19
  634. package/dist/tutorials/Channels.md +42 -35
  635. package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
  636. package/dist/tutorials/Create-Article.md +114 -0
  637. package/dist/tutorials/Create-Extras.md +112 -0
  638. package/dist/tutorials/Create-Images.md +165 -0
  639. package/dist/tutorials/Create-Narration.md +178 -0
  640. package/dist/tutorials/Create-Overview.md +244 -0
  641. package/dist/tutorials/Create-Video-and-Style.md +144 -0
  642. package/dist/tutorials/Docker.md +3 -3
  643. package/dist/tutorials/Document-Themes.md +32 -32
  644. package/dist/tutorials/Editing-a-Project.md +94 -94
  645. package/dist/tutorials/FAQ.md +18 -18
  646. package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
  647. package/dist/tutorials/Home-and-Projects.md +115 -71
  648. package/dist/tutorials/Home.md +33 -33
  649. package/dist/tutorials/Install.md +14 -13
  650. package/dist/tutorials/Intros-and-Outros.md +33 -34
  651. package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
  652. package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
  653. package/dist/tutorials/Models.md +53 -21
  654. package/dist/tutorials/Multiple-Voices.md +41 -40
  655. package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
  656. package/dist/tutorials/Notifications.md +27 -17
  657. package/dist/tutorials/Other-Languages.md +15 -15
  658. package/dist/tutorials/PDF-Documents.md +38 -29
  659. package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
  660. package/dist/tutorials/Project-Page.md +118 -120
  661. package/dist/tutorials/Prompts-and-Templates.md +84 -0
  662. package/dist/tutorials/Prompts.md +48 -44
  663. package/dist/tutorials/Providers-and-Keys.md +105 -98
  664. package/dist/tutorials/Publishing-to-YouTube.md +31 -31
  665. package/dist/tutorials/Recovery-and-Retries.md +31 -31
  666. package/dist/tutorials/Remote-Access.md +63 -0
  667. package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
  668. package/dist/tutorials/Schedules.md +46 -27
  669. package/dist/tutorials/Settings-Reference.md +194 -174
  670. package/dist/tutorials/Shorts.md +34 -33
  671. package/dist/tutorials/Start-at-Login.md +3 -3
  672. package/dist/tutorials/Studio-Extension.md +22 -24
  673. package/dist/tutorials/Template-Studio.md +128 -0
  674. package/dist/tutorials/Templates.md +51 -77
  675. package/dist/tutorials/Trash.md +4 -4
  676. package/dist/tutorials/Troubleshooting.md +44 -44
  677. package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
  678. package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
  679. package/dist/tutorials/Video-Editing.md +34 -34
  680. package/dist/tutorials/Where-Your-Files-Live.md +10 -10
  681. package/dist/tutorials/YouTube-Description.md +26 -26
  682. package/dist/tutorials/Your-First-Short.md +35 -35
  683. package/dist/tutorials/_Sidebar.md +19 -14
  684. package/dist/updater/work-in-progress.js +42 -12
  685. package/dist/web/assets/index-BNftKQrA.css +1 -0
  686. package/dist/web/assets/index-Cpa58woP.js +459 -0
  687. package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
  688. package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
  689. package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
  690. package/dist/web/index.html +2 -2
  691. package/package.json +6 -2
  692. package/dist/slices/article/provided-entries.js +0 -66
  693. package/dist/slices/article/run.js +0 -145
  694. package/dist/slices/images/run.js +0 -234
  695. package/dist/slices/narration/legacy-plan.js +0 -58
  696. package/dist/slices/narration/run.js +0 -270
  697. package/dist/slices/rebuild/runtime-actions.js +0 -90
  698. package/dist/slices/research/run.js +0 -224
  699. package/dist/slices/subtitles/prepare.js +0 -194
  700. package/dist/slices/subtitles/transcript.js +0 -34
  701. package/dist/slices/thumbnail/run.js +0 -219
  702. package/dist/slices/video/audio-inputs.js +0 -33
  703. package/dist/slices/video/reuse-audio.js +0 -62
  704. package/dist/slices/video/run.js +0 -78
  705. package/dist/slices/video/write-export.js +0 -158
  706. package/dist/slices/video/write-subtitles.js +0 -59
  707. package/dist/tutorials/Library-Overview.md +0 -76
  708. package/dist/tutorials/Play-Images.md +0 -161
  709. package/dist/tutorials/Play-Narration.md +0 -167
  710. package/dist/tutorials/Play-Outputs.md +0 -109
  711. package/dist/tutorials/Play-Overview.md +0 -227
  712. package/dist/tutorials/Play-Title-and-Article.md +0 -136
  713. package/dist/tutorials/Play-Video-and-Style.md +0 -143
  714. package/dist/web/assets/index-BadsoORk.css +0 -1
  715. package/dist/web/assets/index-D8LqNsCl.js +0 -326
  716. package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
  717. package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
  718. package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
@@ -8,6 +8,7 @@ import { transact } from "../../kernel/db/tx.js";
8
8
  import { derive } from "../../kernel/runner/graph.js";
9
9
  import { listProjects, projectById, stagesOf } from "../../slices/admission/repo.js";
10
10
  import { channelById, projectChannels } from "../../slices/channels/repo.js";
11
+ import { isSampleProject } from "../../slices/onboarding/sample.js";
11
12
  import { scheduleById, scheduleRunOfProject } from "../../slices/schedules/repo.js";
12
13
  import { readSetting, writeSetting } from "../../slices/settings/repo.js";
13
14
  import { findDownload } from "../../slices/storage/downloads.js";
@@ -19,17 +20,16 @@ import { writeUploadPick } from "../../slices/studio/pick.js";
19
20
  import { postingPlanSchema, readPlan, readStoredPlan, weekdayIn, writePlan, } from "../../slices/studio/plan.js";
20
21
  import { leadHoursMax, seriesOf } from "../../slices/studio/plan-model.js";
21
22
  import { enqueueFill, fillOnly, readFillQueue, removeFill, } from "../../slices/studio/queue.js";
22
- import { allReleases, freeSlots, planReleases, readLeadHours, releasesOf, setRelease, swapReleases, writeLeadHours, } from "../../slices/studio/releases.js";
23
- import { autoCommentKey, bearerToken, isExtensionOrigin, pairStudioExtension, readChannelPlaylists, readStudioPlaylists, resetStudioPairing, saveProjectPlaylists, saveRealFootage, saveStudioPlaylists, studioPairing, studioPlaylistsMax, studioPlaylistsProblem, studioRequestAllowed, } from "../../slices/studio/settings.js";
23
+ import { allReleases, freeSlots, lineZone, planReleases, readLeadHours, releasesOf, setRelease, swapReleases, writeLeadHours, } from "../../slices/studio/releases.js";
24
+ import { autoCommentKey, bearerToken, pairStudioExtension, readChannelPlaylists, readStudioPlaylists, resetStudioPairing, saveProjectPlaylists, saveRealFootage, saveStudioPlaylists, studioPairing, studioPlaylistsMax, studioPlaylistsProblem, } from "../../slices/studio/settings.js";
24
25
  import { abResults, abVariantSchema, saveAbResult } from "../../slices/studio/stats.js";
25
- import { confirmUpload, doneVideos, forgetVideo, projectVideos, recordedVideos, recordVideo, setChecks, setTaskState, videoIdOf, videoIdPattern, videoOf, waitingTasks, } from "../../slices/studio/videos.js";
26
+ import { confirmUpload, doneVideos, forgetVideo, projectVideos, recordedVideos, recordVideo, setAbState, setChecks, setTaskState, videoIdOf, videoIdPattern, videoOf, waitingTasks, } from "../../slices/studio/videos.js";
26
27
  import { setAsideProjects, uploadedProjects } from "../../slices/uploads/repo.js";
27
- import { onInvalid, problem, titleOf } from "./problem.js";
28
- const id = z
29
- .string()
30
- .min(1)
31
- .max(64)
32
- .regex(/^[0-9A-Za-z_-]+$/);
28
+ import { extensionAuth } from "./extension-auth.js";
29
+ import { idString } from "./ids.js";
30
+ import { onInvalid, problem } from "./problem.js";
31
+ import { fromSlopifyPage } from "./same-page.js";
32
+ const id = idString;
33
33
  const projectParam = z.object({ projectId: id });
34
34
  const fileParam = z.object({
35
35
  projectId: id,
@@ -80,8 +80,9 @@ const videoLinkBody = z.object({ short: shortField, link: z.string().max(500) })
80
80
  const slotBody = z.object({
81
81
  slot: z.object({ row: z.string().min(1).max(20), longAt: z.string().max(40) }).nullable(),
82
82
  });
83
+ // "ab": the extension set up (or couldn't set up) A/B Testing on the video's Details page.
83
84
  const taskResultBody = z.object({
84
- task: z.enum(["finish", "comment"]),
85
+ task: z.enum(["finish", "comment", "ab"]),
85
86
  projectId: id,
86
87
  short: shortField,
87
88
  ok: z.boolean(),
@@ -122,34 +123,24 @@ export function studioRoutes(deps) {
122
123
  return project === undefined ? [] : [{ ...entry, projectTitle: project.title }];
123
124
  });
124
125
  const samePage = (c) => {
125
- const origin = c.req.header("origin");
126
- if (origin === undefined || origin === new URL(c.req.url).origin)
126
+ if (fromSlopifyPage(c, deps.remoteAccess?.remoteHost))
127
127
  return undefined;
128
128
  return problem(c, {
129
129
  status: 403,
130
- title: titleOf(403),
131
130
  detail: "For your safety, YouTube Studio settings can only be changed from the Slopify page itself. Open Slopify and try again there.",
132
131
  });
133
132
  };
134
- // The CORS headers for the extension: its own origin, echoed only when it is allowed.
135
- const allowOrigin = (c, pairing) => {
136
- const origin = c.req.header("origin");
137
- const paired = studioPairing(deps.db).origin;
138
- c.header("Vary", "Origin");
139
- if (!isExtensionOrigin(origin))
140
- return;
141
- if (origin === paired || pairing) {
142
- c.header("Access-Control-Allow-Origin", origin);
143
- c.header("Access-Control-Allow-Headers", "authorization, content-type");
144
- c.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
145
- c.header("Access-Control-Max-Age", "600");
146
- }
147
- };
148
- const refused = (c) => problem(c, {
149
- status: 401,
150
- title: "Unauthorized",
151
- detail: "The Slopify Studio extension isn't paired with this Slopify. Copy the pairing token from Slopify's Settings → YouTube Studio into the extension's options and press Pair.",
152
- });
133
+ const { allowOrigin, refused, allowed: extAllowed } = extensionAuth(deps.db);
134
+ // The bundled samples are read-only and never go to YouTube, so nothing here writes a
135
+ // release, a link, a pick or a fill for one, as the project routes refuse its writes
136
+ // (`app.ts`). Reading them stays open: the sample's page shows what a finished video offers.
137
+ const sampleRefused = (c, ...projectIds) => projectIds.some((one) => isSampleProject(deps.db, one))
138
+ ? problem(c, {
139
+ status: 409,
140
+ detail: "This is a sample project, which is read-only: it isn't on YouTube and can't be. Press Make my own copy at the top of its page to post your own.",
141
+ extensions: { reason: "sample-read-only" },
142
+ })
143
+ : undefined;
153
144
  // A project's place in the plan: its series, and the schedule and run day that made it.
154
145
  const whoOf = (projectId, series) => {
155
146
  const run = scheduleRunOfProject(deps.db, projectId);
@@ -165,7 +156,10 @@ export function studioRoutes(deps) {
165
156
  // A project ready to upload gets its release times from the posting plan when its upload is
166
157
  // prepared (`releases.ts`).
167
158
  // A long video already on YouTube keeps when it went up; its shorts still get times.
159
+ // A sample never gets a time: it isn't going to YouTube.
168
160
  const planned = (projectId) => {
161
+ if (isSampleProject(deps.db, projectId))
162
+ return;
169
163
  const long = videoOf(deps.db, projectId, null);
170
164
  const out = long?.uploadState === "done" ? long.recordedAt : undefined;
171
165
  const result = uploadPack(deps, projectId);
@@ -187,18 +181,18 @@ export function studioRoutes(deps) {
187
181
  .toSorted(),
188
182
  });
189
183
  const slotChoices = (projectId, series) => freeSlots(deps.db, readPlan(deps.db), whoOf(projectId, series), deps.clock.now(), 9);
190
- // Finished projects not marked uploaded: the popup's list and the calendar's candidates.
184
+ // Finished projects not marked uploaded: the popup's list and the calendar's candidates. The
185
+ // samples are never among them.
191
186
  const finishedProjects = () => {
192
187
  const uploads = uploadedProjects(deps.db);
193
188
  const aside = setAsideProjects(deps.db);
194
189
  return listProjects(deps.db).filter((project) => {
195
- if (uploads.has(project.id))
190
+ if (uploads.has(project.id) || isSampleProject(deps.db, project.id))
196
191
  return false;
197
192
  const state = derive(stagesOf(deps.db, project.id), project.paused === true);
198
193
  return (state === "done" || state === "partial" || (state === "pending" && aside.has(project.id)));
199
194
  });
200
195
  };
201
- const extAllowed = (c) => studioRequestAllowed(deps.db, bearerToken(c.req.header("authorization")), c.req.header("origin"));
202
196
  return (new Hono()
203
197
  .get("/settings", (c) => {
204
198
  // The token is read only by the Slopify page, never by another origin's.
@@ -267,8 +261,13 @@ export function studioRoutes(deps) {
267
261
  return unknownProject(c);
268
262
  const leadMs = readLeadHours(deps.db) * 3_600_000;
269
263
  const releases = releasesOf(deps.db, projectId);
264
+ // The long video is on YouTube once its link is known or the project is marked
265
+ // uploaded (one fact, `videos.ts`); a short only by its own link.
266
+ const uploaded = uploadedProjects(deps.db).has(projectId);
270
267
  return c.json({
271
268
  leadHours: readLeadHours(deps.db),
269
+ // The zone its times are read in: its line's schedule's, else the plan's.
270
+ timeZone: lineZone(readPlan(deps.db), releases.find((release) => release.short === 0)?.line ?? null),
272
271
  items: pack.pack.items
273
272
  .filter((item) => item.video !== null || item.kind === "video")
274
273
  .map((item) => {
@@ -279,7 +278,8 @@ export function studioRoutes(deps) {
279
278
  title: item.title,
280
279
  at: at === "" ? null : at,
281
280
  uploadBy: at === "" ? null : new Date(Date.parse(at) - leadMs).toISOString(),
282
- onYoutube: videoOf(deps.db, projectId, item.short ?? null)?.uploadState === "done",
281
+ onYoutube: videoOf(deps.db, projectId, item.short ?? null)?.uploadState === "done" ||
282
+ (item.kind === "video" && uploaded),
283
283
  };
284
284
  }),
285
285
  free: slotChoices(projectId, pack.pack.series),
@@ -292,11 +292,13 @@ export function studioRoutes(deps) {
292
292
  return denied;
293
293
  const { projectId } = c.req.valid("param");
294
294
  const other = c.req.valid("json").with;
295
+ const sample = sampleRefused(c, projectId, other);
296
+ if (sample !== undefined)
297
+ return sample;
295
298
  const swapped = transact(deps.db, () => swapReleases(deps.db, projectId, other, deps.clock.now()));
296
299
  if (!swapped)
297
300
  return problem(c, {
298
301
  status: 409,
299
- title: titleOf(409),
300
302
  detail: "Both videos need a release time to swap. Give this one a time first, or reload the page and pick another video.",
301
303
  });
302
304
  planned(projectId);
@@ -316,6 +318,9 @@ export function studioRoutes(deps) {
316
318
  if (denied !== undefined)
317
319
  return denied;
318
320
  const { projectId } = c.req.valid("param");
321
+ const sample = sampleRefused(c, projectId);
322
+ if (sample !== undefined)
323
+ return sample;
319
324
  if (uploadPack(deps, projectId).ok === false)
320
325
  return unknownProject(c);
321
326
  const { short, at, line } = c.req.valid("json");
@@ -331,14 +336,12 @@ export function studioRoutes(deps) {
331
336
  if (channelId !== undefined && channelById(deps.db, channelId) === undefined)
332
337
  return problem(c, {
333
338
  status: 404,
334
- title: titleOf(404),
335
- detail: "The playlists weren't saved: that channel no longer exists. Reload Settings → YouTube Studio and pick the channel again.",
339
+ detail: "The playlists weren't saved: that channel no longer exists. Reload Settings → YouTube Studio and posting and pick the channel again.",
336
340
  });
337
341
  const invalid = studioPlaylistsProblem(raw);
338
342
  if (invalid !== undefined)
339
343
  return problem(c, {
340
344
  status: 400,
341
- title: titleOf(400),
342
345
  detail: invalid,
343
346
  extensions: { fields: [{ field: "playlists", message: invalid }] },
344
347
  });
@@ -361,12 +364,15 @@ export function studioRoutes(deps) {
361
364
  return unknownProject(c);
362
365
  return c.json({ ...result.pack, slotChoices: slotChoices(projectId, result.pack.series) });
363
366
  })
364
- // Prepare upload's slot picker: another free slot of the plan, or none.
367
+ // Copy for YouTube Studio's Release picker: another free time of the plan, or none.
365
368
  .put("/packs/:projectId/slot", zValidator("param", projectParam, onInvalid), zValidator("json", slotBody, onInvalid), (c) => {
366
369
  const denied = samePage(c);
367
370
  if (denied !== undefined)
368
371
  return denied;
369
372
  const { projectId } = c.req.valid("param");
373
+ const sample = sampleRefused(c, projectId);
374
+ if (sample !== undefined)
375
+ return sample;
370
376
  const wanted = c.req.valid("json").slot;
371
377
  const before = uploadPack(deps, projectId);
372
378
  if (!before.ok)
@@ -378,8 +384,7 @@ export function studioRoutes(deps) {
378
384
  if (!free)
379
385
  return problem(c, {
380
386
  status: 409,
381
- title: titleOf(409),
382
- detail: "That slot is taken or past. Reload Prepare upload and choose one of the slots it lists.",
387
+ detail: "That time is taken or past. Close Copy for YouTube Studio, open it again and choose one of the times it lists.",
383
388
  });
384
389
  setRelease(deps.db, projectId, 0, wanted.longAt, wanted.row, deps.clock.now());
385
390
  }
@@ -389,37 +394,47 @@ export function studioRoutes(deps) {
389
394
  ? c.json({ ...result.pack, slotChoices: slotChoices(projectId, result.pack.series) })
390
395
  : unknownProject(c);
391
396
  })
392
- // Prepare upload's AI use tick: the project's uploaded clips are real footage.
397
+ // Copy for YouTube Studio's AI use tick: the project's uploaded clips are real footage.
393
398
  .put("/packs/:projectId/real-footage", zValidator("param", projectParam, onInvalid), zValidator("json", realFootageBody, onInvalid), (c) => {
394
399
  const denied = samePage(c);
395
400
  if (denied !== undefined)
396
401
  return denied;
397
402
  const { projectId } = c.req.valid("param");
403
+ const sample = sampleRefused(c, projectId);
404
+ if (sample !== undefined)
405
+ return sample;
398
406
  if (!uploadPack(deps, projectId).ok)
399
407
  return unknownProject(c);
400
408
  saveRealFootage(deps.db, projectId, c.req.valid("json").realFootage);
401
409
  const result = uploadPack(deps, projectId);
402
410
  return result.ok ? c.json(result.pack) : unknownProject(c);
403
411
  })
404
- // Prepare upload's pick: which title and thumbnail the upload carries; the A/B test tries
412
+ // Copy for YouTube Studio's pick: which title and thumbnail the upload carries; the A/B test tries
405
413
  // the others once the video is public.
406
414
  .put("/packs/:projectId/pick", zValidator("param", projectParam, onInvalid), zValidator("json", pickBody, onInvalid), (c) => {
407
415
  const denied = samePage(c);
408
416
  if (denied !== undefined)
409
417
  return denied;
410
418
  const { projectId } = c.req.valid("param");
419
+ const sample = sampleRefused(c, projectId);
420
+ if (sample !== undefined)
421
+ return sample;
411
422
  if (!uploadPack(deps, projectId).ok)
412
423
  return unknownProject(c);
413
424
  writeUploadPick(deps.db, projectId, c.req.valid("json"));
414
425
  const result = uploadPack(deps, projectId);
415
426
  return result.ok ? c.json(result.pack) : unknownProject(c);
416
427
  })
417
- // Prepare upload's playlist ticks: which of the channel's playlists this project goes into.
428
+ // Copy for YouTube Studio's playlist ticks: which of the channel's playlists this project
429
+ // goes into.
418
430
  .put("/packs/:projectId/playlists", zValidator("param", projectParam, onInvalid), zValidator("json", projectPlaylistsBody, onInvalid), (c) => {
419
431
  const denied = samePage(c);
420
432
  if (denied !== undefined)
421
433
  return denied;
422
434
  const { projectId } = c.req.valid("param");
435
+ const sample = sampleRefused(c, projectId);
436
+ if (sample !== undefined)
437
+ return sample;
423
438
  if (!uploadPack(deps, projectId).ok)
424
439
  return unknownProject(c);
425
440
  saveProjectPlaylists(deps.db, projectId, c.req.valid("json").playlists);
@@ -431,6 +446,9 @@ export function studioRoutes(deps) {
431
446
  if (denied !== undefined)
432
447
  return denied;
433
448
  const { projectId } = c.req.valid("param");
449
+ const sample = sampleRefused(c, projectId);
450
+ if (sample !== undefined)
451
+ return sample;
434
452
  const { short } = c.req.valid("json");
435
453
  const result = uploadPack(deps, projectId);
436
454
  if (!result.ok)
@@ -438,8 +456,7 @@ export function studioRoutes(deps) {
438
456
  if (packItem(result.pack, short) === undefined)
439
457
  return problem(c, {
440
458
  status: 404,
441
- title: titleOf(404),
442
- detail: `This project has no short ${String(short)}. Reload the project page and press Prepare upload again.`,
459
+ detail: `This project has no short ${String(short)}. Reload the project page and press Copy for YouTube Studio again.`,
443
460
  });
444
461
  const queue = enqueueFill(deps.db, projectId, short ?? null, deps.clock.now());
445
462
  return c.json({ chosen: { projectId, short: short ?? null }, queue: queueView(queue) });
@@ -450,22 +467,25 @@ export function studioRoutes(deps) {
450
467
  // The YouTube videos a project's uploads became, and their A/B tests.
451
468
  .get("/videos/:projectId", zValidator("param", projectParam, onInvalid), (c) => c.json({ videos: projectVideos(deps.db, c.req.valid("param").projectId) }))
452
469
  // A video's link pasted in Slopify, for an upload made without the extension; an empty
453
- // link forgets it.
470
+ // link forgets it. The long video's link marks the project uploaded, and forgetting it
471
+ // takes the mark off (`videos.ts`).
454
472
  .put("/videos/:projectId", zValidator("param", projectParam, onInvalid), zValidator("json", videoLinkBody, onInvalid), (c) => {
455
473
  const wrong = samePage(c);
456
474
  if (wrong !== undefined)
457
475
  return wrong;
458
476
  const { projectId } = c.req.valid("param");
477
+ const sample = sampleRefused(c, projectId);
478
+ if (sample !== undefined)
479
+ return sample;
459
480
  const { short, link } = c.req.valid("json");
460
481
  if (link.trim() === "") {
461
- forgetVideo(deps.db, projectId, short ?? null);
482
+ forgetVideo(deps.db, projectId, short ?? null, deps.clock.now().toISOString());
462
483
  return c.json({ videos: projectVideos(deps.db, projectId) });
463
484
  }
464
485
  const videoId = videoIdOf(link);
465
486
  if (videoId === undefined)
466
487
  return problem(c, {
467
488
  status: 400,
468
- title: titleOf(400),
469
489
  detail: "That isn't a YouTube video link. Copy the video's link from YouTube Studio (Details → Video link, like https://youtu.be/…) and paste it again.",
470
490
  });
471
491
  recordVideo(deps.db, projectId, short ?? null, videoId, deps.clock.now().toISOString());
@@ -486,7 +506,7 @@ export function studioRoutes(deps) {
486
506
  queue: queueView(removeFill(deps.db, projectId, short ?? null, deps.clock.now())),
487
507
  });
488
508
  })
489
- // The extension's own zips, for Settings' and Prepare upload's Download.
509
+ // The extension's own zips, for Settings' and Copy for YouTube Studio's Download.
490
510
  .get("/extension/:file", zValidator("param", extensionParam, onInvalid), (c) => {
491
511
  const { file } = c.req.valid("param");
492
512
  const name = extensionFiles[file] ?? "";
@@ -495,7 +515,6 @@ export function studioRoutes(deps) {
495
515
  if (path === undefined || stat === undefined || !stat.isFile())
496
516
  return problem(c, {
497
517
  status: 404,
498
- title: titleOf(404),
499
518
  detail: "The Slopify Studio extension isn't included in this copy of Slopify, so there is nothing to download. Update or reinstall Slopify; when running from the repository, run `npm run build` at its root, which builds the extension too.",
500
519
  });
501
520
  return c.body(Readable.toWeb(createReadStream(path)), 200, {
@@ -522,8 +541,7 @@ export function studioRoutes(deps) {
522
541
  ? refused(c)
523
542
  : problem(c, {
524
543
  status: 403,
525
- title: titleOf(403),
526
- detail: "Only the Slopify Studio browser extension can pair with Slopify. Install it (see Settings → YouTube Studio) and pair from its options page.",
544
+ detail: "Only the Slopify Studio browser extension can pair with Slopify. Install it (see Settings → YouTube Studio and posting) and pair from its options page.",
527
545
  });
528
546
  // The origin is paired now; the response carries its headers.
529
547
  allowOrigin(c, false);
@@ -531,7 +549,7 @@ export function studioRoutes(deps) {
531
549
  })
532
550
  .get("/ext/pack", (c) => {
533
551
  allowOrigin(c, false);
534
- if (!studioRequestAllowed(deps.db, bearerToken(c.req.header("authorization")), c.req.header("origin")))
552
+ if (!extAllowed(c))
535
553
  return refused(c);
536
554
  // The first waiting item that can still be filled; one whose project or short is gone
537
555
  // leaves the queue.
@@ -549,17 +567,16 @@ export function studioRoutes(deps) {
549
567
  }
550
568
  return problem(c, {
551
569
  status: 404,
552
- title: titleOf(404),
553
570
  detail: gone > 0
554
- ? "The project chosen to fill in is gone. In Slopify, open the project again, press Prepare upload, then Fill in YouTube Studio."
555
- : "Nothing is waiting to be filled in. In Slopify, open the project, press Prepare upload, then Fill in YouTube Studio.",
571
+ ? "The project chosen to fill in is gone. In Slopify, open the project again, press Copy for YouTube Studio, then Fill in YouTube Studio."
572
+ : "Nothing is waiting to be filled in. In Slopify, open the project, press Copy for YouTube Studio, then Fill in YouTube Studio.",
556
573
  });
557
574
  })
558
575
  // The extension filled an item: it leaves the queue, so the next upload dialog takes the
559
576
  // next one.
560
577
  .post("/ext/filled", zValidator("json", queueItemBody, onInvalid), (c) => {
561
578
  allowOrigin(c, false);
562
- if (!studioRequestAllowed(deps.db, bearerToken(c.req.header("authorization")), c.req.header("origin")))
579
+ if (!extAllowed(c))
563
580
  return refused(c);
564
581
  const { projectId, short } = c.req.valid("json");
565
582
  const left = removeFill(deps.db, projectId, short ?? null, deps.clock.now());
@@ -623,11 +640,13 @@ export function studioRoutes(deps) {
623
640
  if (!extAllowed(c))
624
641
  return refused(c);
625
642
  const { projectId, short } = c.req.valid("json");
643
+ const sample = sampleRefused(c, projectId);
644
+ if (sample !== undefined)
645
+ return sample;
626
646
  const result = uploadPack(deps, projectId);
627
647
  if (!result.ok || packItem(result.pack, short ?? undefined) === undefined)
628
648
  return problem(c, {
629
649
  status: 404,
630
- title: titleOf(404),
631
650
  detail: "That upload isn't in the project any more. Open the popup again.",
632
651
  });
633
652
  planned(projectId);
@@ -641,6 +660,9 @@ export function studioRoutes(deps) {
641
660
  if (!extAllowed(c))
642
661
  return refused(c);
643
662
  const { projectId } = c.req.valid("json");
663
+ const sample = sampleRefused(c, projectId);
664
+ if (sample !== undefined)
665
+ return sample;
644
666
  planned(projectId);
645
667
  const result = uploadPack(deps, projectId);
646
668
  if (!result.ok)
@@ -692,10 +714,14 @@ export function studioRoutes(deps) {
692
714
  if (!extAllowed(c))
693
715
  return refused(c);
694
716
  const { task, projectId, short, ok, message } = c.req.valid("json");
695
- setTaskState(deps.db, task, projectId, short ?? null, ok ? "done" : "failed", message);
717
+ if (task === "ab")
718
+ setAbState(deps.db, projectId, short ?? null, ok ? "started" : "failed", message, deps.clock.now().toISOString());
719
+ else
720
+ setTaskState(deps.db, task, projectId, short ?? null, ok ? "done" : "failed", message);
696
721
  return c.json({ ok: true });
697
722
  })
698
- // The long videos whose A/B test is running, for the extension to read each result.
723
+ // The long videos whose A/B test is running (the extension reported setting it up), for
724
+ // the extension to read each result.
699
725
  .get("/ext/known-videos", (c) => {
700
726
  allowOrigin(c, false);
701
727
  if (!extAllowed(c))
@@ -740,7 +766,7 @@ export function studioRoutes(deps) {
740
766
  for (const { projectId, short, videoId } of body.videos) {
741
767
  if (videoOf(deps.db, projectId, short ?? null)?.videoId !== videoId)
742
768
  continue;
743
- forgetVideo(deps.db, projectId, short ?? null);
769
+ forgetVideo(deps.db, projectId, short ?? null, deps.clock.now().toISOString());
744
770
  deps.hub.emit(projectId, { type: "project.updated", projectId });
745
771
  forgotten++;
746
772
  }
@@ -795,7 +821,7 @@ export function studioRoutes(deps) {
795
821
  })
796
822
  .get("/ext/files/:projectId/:asset", zValidator("param", fileParam, onInvalid), (c) => {
797
823
  allowOrigin(c, false);
798
- if (!studioRequestAllowed(deps.db, bearerToken(c.req.header("authorization")), c.req.header("origin")))
824
+ if (!extAllowed(c))
799
825
  return refused(c);
800
826
  const { projectId, asset } = c.req.valid("param");
801
827
  // Only a pack's own files, its videos, thumbnails and captions: the extension puts the
@@ -810,7 +836,6 @@ export function studioRoutes(deps) {
810
836
  if (found === undefined || !found.ok)
811
837
  return problem(c, {
812
838
  status: 404,
813
- title: titleOf(404),
814
839
  detail: "That file isn't in the project's upload pack any more. Reload the project in Slopify and press Fill in YouTube Studio again.",
815
840
  });
816
841
  return c.body(Readable.toWeb(createReadStream(found.download.path)), 200, {
@@ -824,8 +849,9 @@ export function studioRoutes(deps) {
824
849
  // (a short's related video, the long video's end screen and captions, unless they went in
825
850
  // during the upload) wait for the extension, the pinned comment for the video to be public.
826
851
  function confirmVideoUpload(deps, projectId, short, videoId, captionsAdded = false) {
827
- if (!confirmUpload(deps.db, projectId, short, videoId))
828
- recordVideo(deps.db, projectId, short, videoId, deps.clock.now().toISOString());
852
+ const at = deps.clock.now().toISOString();
853
+ if (!confirmUpload(deps.db, projectId, short, videoId, at))
854
+ recordVideo(deps.db, projectId, short, videoId, at);
829
855
  const confirmedPack = uploadPack(deps, projectId);
830
856
  const confirmedItem = confirmedPack.ok
831
857
  ? packItem(confirmedPack.pack, short ?? undefined)
@@ -843,7 +869,6 @@ function confirmVideoUpload(deps, projectId, short, videoId, captionsAdded = fal
843
869
  function unknownProject(c) {
844
870
  return problem(c, {
845
871
  status: 404,
846
- title: titleOf(404),
847
872
  detail: "This project no longer exists. Go back to Projects and open it again.",
848
873
  });
849
874
  }
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
  import { StylePreviewError } from "../../slices/style-preview/render.js";
5
5
  import { stylePreviewRequestSchema } from "../../slices/style-preview/schema.js";
6
6
  import { fileResponse } from "./byte-range.js";
7
- import { onInvalid, problem, titleOf } from "./problem.js";
7
+ import { onInvalid, problem } from "./problem.js";
8
8
  export { byteRange } from "./byte-range.js";
9
9
  // POST renders (or finds) the preview for a set of caption and Look settings and answers where
10
10
  // to play it; GET streams the mp4, with byte ranges so the browser's player can seek.
@@ -31,7 +31,6 @@ export function stylePreviewRoutes(deps) {
31
31
  const status = error.reason === "font-missing" ? 409 : error.reason === "ffmpeg-missing" ? 503 : 500;
32
32
  return problem(c, {
33
33
  status,
34
- title: titleOf(status),
35
34
  detail: error.message,
36
35
  extensions: { reason: error.reason },
37
36
  });
@@ -42,8 +41,7 @@ export function stylePreviewRoutes(deps) {
42
41
  if (saved === undefined)
43
42
  return problem(c, {
44
43
  status: 404,
45
- title: titleOf(404),
46
- detail: "This style preview is no longer saved; Slopify keeps only the most recent ones. Press Render again to make it.",
44
+ detail: "This style preview is no longer saved; Slopify keeps only the most recent ones. Press Make the preview again to make it.",
47
45
  });
48
46
  return fileResponse(c, saved.path, saved.bytes, {
49
47
  "content-type": "video/mp4",
@@ -55,7 +53,6 @@ export function stylePreviewRoutes(deps) {
55
53
  function unavailable(c) {
56
54
  return problem(c, {
57
55
  status: 503,
58
- title: titleOf(503),
59
- detail: "The style preview isn't available because this Slopify started without its video renderer. Restart Slopify, then press Render again.",
56
+ detail: "The style preview isn't available because this Slopify started without its video renderer. Restart Slopify, then press Make the preview again.",
60
57
  });
61
58
  }
@@ -1,22 +1,8 @@
1
1
  import { zValidator } from "@hono/zod-validator";
2
2
  import { Hono } from "hono";
3
- import { z } from "zod";
4
- import { onInvalid, problem, titleOf } from "./problem.js";
5
- const idParam = z.object({
6
- id: z
7
- .string()
8
- .min(1)
9
- .max(64)
10
- .regex(/^[0-9A-Za-z_-]+$/),
11
- });
3
+ import { idParam } from "./ids.js";
4
+ import { onInvalid } from "./problem.js";
5
+ import { revisionRequired } from "./refusal.js";
12
6
  export function subtitleRoutes(_deps) {
13
7
  return new Hono().patch("/:id/subtitles", zValidator("param", idParam, onInvalid), revisionRequired);
14
8
  }
15
- function revisionRequired(c) {
16
- return problem(c, {
17
- status: 409,
18
- title: titleOf(409),
19
- detail: "This change is now made from the Edit tab. Choose Edit project, save your change, then use Choose what to remake (in the project's More menu).",
20
- extensions: { reason: "revision-required" },
21
- });
22
- }