@gentbajko/slopify 3.25.8 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/account-limits.js +30 -0
- package/dist/adapters/alignment/cache.js +3 -5
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +0 -3
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/worker.js +2 -1
- package/dist/adapters/explain.js +0 -3
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +19 -3
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/codex.js +36 -4
- package/dist/adapters/image/fal.js +17 -50
- package/dist/adapters/image/google.js +31 -55
- package/dist/adapters/image/openai.js +20 -47
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +18 -47
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +53 -2
- package/dist/adapters/llm/antigravity.js +4 -0
- package/dist/adapters/llm/assistant-cli.js +388 -0
- package/dist/adapters/llm/child-session.js +83 -0
- package/dist/adapters/llm/claude-code-models.js +51 -98
- package/dist/adapters/llm/claude-code.js +13 -2
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/openrouter.js +10 -47
- package/dist/adapters/llm/run-cli.js +6 -22
- package/dist/adapters/llm/sse-lines.js +5 -1
- package/dist/adapters/model-access.js +37 -0
- package/dist/adapters/tts/cartesia.js +15 -46
- package/dist/adapters/tts/elevenlabs.js +20 -52
- package/dist/adapters/tts/gemini.js +19 -40
- package/dist/adapters/tts/inworld-async.js +4 -1
- package/dist/adapters/tts/inworld.js +3 -0
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/assets/models-lifecycle.yaml +6 -0
- package/dist/assistant-wiring.js +60 -0
- package/dist/autopilot-timers.js +59 -0
- package/dist/catalog/lifecycle.js +52 -0
- package/dist/catalog/propose.js +204 -0
- package/dist/catalog/registry.js +3 -1
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +39 -0
- package/dist/edge/autostart/native.js +3 -5
- package/dist/edge/autostart/prompt.js +2 -1
- package/dist/edge/autostart/service.js +3 -4
- package/dist/edge/cli-args.js +3 -4
- package/dist/edge/cli.js +2 -1
- package/dist/edge/docker-install/apply.js +11 -11
- package/dist/edge/docker-install/run.js +2 -1
- package/dist/edge/docker-install/state.js +4 -3
- package/dist/edge/docker-install/tree.js +2 -4
- package/dist/edge/docker-install/volume.js +3 -2
- package/dist/edge/docker.js +5 -4
- package/dist/edge/events/hub.js +5 -5
- package/dist/edge/http/actions.js +4 -25
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +85 -10
- package/dist/edge/http/assistant.js +127 -0
- package/dist/edge/http/audio-preview.js +4 -11
- package/dist/edge/http/auditions.js +1 -1
- package/dist/edge/http/autostart.js +5 -5
- package/dist/edge/http/backups.js +1 -3
- package/dist/edge/http/channel-memory.js +7 -20
- package/dist/edge/http/channels.js +13 -26
- package/dist/edge/http/checkpoints.js +33 -17
- package/dist/edge/http/design-templates.js +167 -0
- package/dist/edge/http/designs.js +178 -0
- package/dist/edge/http/diagnostics.js +5 -1
- package/dist/edge/http/document-themes.js +2 -11
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +4 -9
- package/dist/edge/http/drafts.js +3 -3
- package/dist/edge/http/entries.js +6 -9
- package/dist/edge/http/extension-auth.js +27 -0
- package/dist/edge/http/external-reviews.js +12 -11
- package/dist/edge/http/files.js +5 -11
- package/dist/edge/http/folder-location.js +1 -4
- package/dist/edge/http/fonts.js +0 -7
- package/dist/edge/http/home.js +38 -13
- package/dist/edge/http/host-cli.js +18 -8
- package/dist/edge/http/ids.js +8 -0
- package/dist/edge/http/keywords.js +35 -0
- package/dist/edge/http/model-checks.js +132 -0
- package/dist/edge/http/narration-peaks.js +5 -13
- package/dist/edge/http/onboarding.js +1 -14
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +4 -12
- package/dist/edge/http/patch-notes.js +4 -10
- package/dist/edge/http/planning.js +2 -6
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +1 -0
- package/dist/edge/http/project-templates.js +29 -17
- package/dist/edge/http/projects.js +2 -12
- package/dist/edge/http/prompts.js +7 -14
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +39 -24
- package/dist/edge/http/refusal.js +29 -0
- package/dist/edge/http/remote-access.js +40 -0
- package/dist/edge/http/remote-guard.js +71 -0
- package/dist/edge/http/request-guard.js +91 -0
- package/dist/edge/http/reviews.js +11 -24
- package/dist/edge/http/revision-files.js +5 -10
- package/dist/edge/http/revisions.js +10 -15
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +59 -32
- package/dist/edge/http/settings.js +22 -13
- package/dist/edge/http/social.js +282 -0
- package/dist/edge/http/staging.js +2 -4
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +23 -23
- package/dist/edge/http/studio.js +23 -50
- package/dist/edge/http/style-preview.js +1 -4
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +433 -0
- package/dist/edge/http/trash.js +10 -11
- package/dist/edge/http/tutorial.js +1 -2
- package/dist/edge/http/tutorials.js +5 -11
- package/dist/edge/http/update.js +7 -13
- package/dist/edge/http/waveform.js +3 -6
- package/dist/edge/http/youtube-edits.js +2 -3
- package/dist/edge/mcp/assistant.js +279 -0
- package/dist/edge/mcp/attribution.js +8 -2
- package/dist/edge/mcp/call-route.js +11 -2
- package/dist/edge/mcp/excluded-routes.js +25 -0
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +5 -3
- package/dist/edge/mcp/library-tools.js +1 -0
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +2 -0
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +14 -2
- package/dist/edge/mcp/tools-planning.js +23 -4
- package/dist/edge/mcp/tools-projects.js +28 -1
- package/dist/edge/mcp/tools-runs.js +14 -1
- package/dist/edge/mcp/tools-schedules.js +28 -4
- package/dist/edge/mcp/tools-setup.js +61 -3
- package/dist/edge/mcp/tools-social.js +46 -0
- package/dist/edge/mcp/tools-storage.js +8 -0
- package/dist/edge/mcp/tools-template-studio.js +166 -0
- package/dist/edge/mcp/tools.js +6 -0
- package/dist/edge/remote-access/cli.js +71 -0
- package/dist/edge/remote-access/model.js +32 -0
- package/dist/edge/remote-access/rules.js +207 -0
- package/dist/edge/remote-access/service.js +203 -0
- package/dist/edge/remote-access/store.js +44 -0
- package/dist/edge/remote-access/views.js +43 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/install.js +2 -2
- package/dist/host-cli/open-folder.js +2 -2
- package/dist/host-cli/paths.js +2 -4
- package/dist/host-cli/server.js +4 -4
- package/dist/host-cli/service.js +3 -3
- package/dist/host-cli/status.js +7 -7
- package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
- package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
- package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
- package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
- package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
- package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
- package/dist/kernel/db/migrations/0070-designs.sql +53 -0
- package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
- package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
- package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
- package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
- package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
- package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
- package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
- package/dist/kernel/db/tx.js +2 -4
- package/dist/kernel/errors.js +27 -1
- package/dist/kernel/json-answer.js +15 -0
- package/dist/kernel/lock.js +4 -9
- package/dist/kernel/paths.js +8 -0
- package/dist/kernel/ports/assistant-cli.js +4 -0
- package/dist/kernel/ports/host-cli.js +3 -0
- package/dist/kernel/ports/interest.js +4 -0
- package/dist/kernel/ports/languages.js +0 -3
- package/dist/kernel/ports/model.js +2 -0
- package/dist/kernel/runner/attempt.js +3 -1
- package/dist/kernel/runner/audition.js +3 -1
- package/dist/kernel/runner/checkpoint-authority.js +2 -1
- package/dist/kernel/runner/index.js +8 -10
- package/dist/kernel/runner/model-access.js +40 -0
- package/dist/kernel/runner/model-check.js +153 -0
- package/dist/kernel/runner/providers.js +15 -10
- package/dist/kernel/runner/standalone.js +9 -5
- package/dist/main.js +334 -19
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/3.25.9.md +5 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/index.json +12 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/repo.js +2 -1
- package/dist/slices/admission/rules.js +43 -8
- package/dist/slices/admission/schema.js +8 -0
- package/dist/slices/admission/stage-writes.js +0 -3
- package/dist/slices/admission/start.js +48 -31
- package/dist/slices/admission/substitute.js +7 -4
- package/dist/slices/agents/attention-posts.js +31 -0
- package/dist/slices/agents/attention-templates.js +33 -0
- package/dist/slices/agents/attention.js +10 -1
- package/dist/slices/agents/connection.js +2 -6
- package/dist/slices/agents/model.js +6 -3
- package/dist/slices/agents/repo.js +15 -7
- package/dist/slices/article/continuation.js +0 -63
- package/dist/slices/article/plain.js +3 -2
- package/dist/slices/article/segments.js +2 -62
- package/dist/slices/assistant/access.js +1 -0
- package/dist/slices/assistant/asks.js +80 -0
- package/dist/slices/assistant/cost.js +54 -0
- package/dist/slices/assistant/gate.js +222 -0
- package/dist/slices/assistant/model.js +79 -0
- package/dist/slices/assistant/names.js +23 -0
- package/dist/slices/assistant/prompt.js +74 -0
- package/dist/slices/assistant/repo.js +74 -0
- package/dist/slices/assistant/tokens.js +18 -0
- package/dist/slices/assistant/turn.js +342 -0
- package/dist/slices/autopilot/buffer.js +76 -0
- package/dist/slices/autopilot/model.js +14 -0
- package/dist/slices/autopilot/notify.js +27 -0
- package/dist/slices/autopilot/repo.js +34 -0
- package/dist/slices/autopilot/signals.js +194 -0
- package/dist/slices/autopilot/warnings.js +85 -0
- package/dist/slices/autopilot/weekly.js +100 -0
- package/dist/slices/backups/files.js +2 -6
- package/dist/slices/backups/repo.js +3 -15
- package/dist/slices/backups/service.js +4 -3
- package/dist/slices/channels/cast-images.js +2 -1
- package/dist/slices/channels/model.js +0 -1
- package/dist/slices/channels/rebrand.js +0 -3
- package/dist/slices/channels/repo.js +4 -0
- package/dist/slices/channels/schema.js +6 -0
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +2 -14
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +0 -26
- package/dist/slices/designs/artwork.js +66 -0
- package/dist/slices/designs/budget.js +23 -0
- package/dist/slices/designs/calls.js +124 -0
- package/dist/slices/designs/check-context.js +38 -0
- package/dist/slices/designs/check-text.js +43 -0
- package/dist/slices/designs/fields.js +88 -0
- package/dist/slices/designs/frames.js +34 -0
- package/dist/slices/designs/gate.js +128 -0
- package/dist/slices/designs/layout.js +293 -0
- package/dist/slices/designs/model.js +35 -0
- package/dist/slices/designs/pdf-job.js +23 -0
- package/dist/slices/designs/pdf-pages.js +30 -0
- package/dist/slices/designs/pdf-runner.js +68 -0
- package/dist/slices/designs/pdf-worker.js +24 -0
- package/dist/slices/designs/picture-size.js +79 -0
- package/dist/slices/designs/pictures.js +55 -0
- package/dist/slices/designs/quote.js +24 -0
- package/dist/slices/designs/render-args.js +36 -0
- package/dist/slices/designs/render-input.js +59 -0
- package/dist/slices/designs/render.js +215 -0
- package/dist/slices/designs/repo.js +101 -0
- package/dist/slices/designs/requests.js +80 -0
- package/dist/slices/designs/runner.js +239 -0
- package/dist/slices/designs/sample-repo.js +91 -0
- package/dist/slices/designs/sample.js +222 -0
- package/dist/slices/designs/schema.js +87 -0
- package/dist/slices/designs/sections.js +44 -0
- package/dist/slices/designs/service.js +233 -0
- package/dist/slices/designs/steps-repo.js +102 -0
- package/dist/slices/designs/studio-kind.js +39 -0
- package/dist/slices/designs/template-checks.js +44 -0
- package/dist/slices/designs/template-make.js +118 -0
- package/dist/slices/designs/templates.js +218 -0
- package/dist/slices/document/fonts.js +2 -1
- package/dist/slices/episodes/summarize.js +5 -1
- package/dist/slices/estimate/index.js +45 -7
- package/dist/slices/fonts/catalog.js +3 -2
- package/dist/slices/fonts/discovery.js +3 -2
- package/dist/slices/fonts/files.js +2 -6
- package/dist/slices/fonts/index.js +0 -1
- package/dist/slices/fonts/sfnt.js +45 -10
- package/dist/slices/images/appearance.js +6 -15
- package/dist/slices/images/scenes.js +100 -27
- package/dist/slices/keywords/digest.js +84 -0
- package/dist/slices/keywords/facts.js +92 -0
- package/dist/slices/keywords/fill.js +80 -0
- package/dist/slices/keywords/model.js +75 -0
- package/dist/slices/keywords/rules.js +187 -0
- package/dist/slices/keywords/schema.js +28 -0
- package/dist/slices/keywords/specs.js +260 -0
- package/dist/slices/keywords/values.js +74 -0
- package/dist/slices/library/lint.js +29 -12
- package/dist/slices/library/model.js +2 -0
- package/dist/slices/library/photorealistic.js +4 -12
- package/dist/slices/library/rename.js +9 -7
- package/dist/slices/library/save.js +14 -10
- package/dist/slices/library/slots.js +37 -11
- package/dist/slices/library/transfer.js +2 -1
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +51 -11
- package/dist/slices/model-checks/cost.js +61 -0
- package/dist/slices/model-checks/listing.js +124 -0
- package/dist/slices/model-checks/model.js +20 -0
- package/dist/slices/model-checks/record.js +32 -0
- package/dist/slices/model-checks/repo.js +120 -0
- package/dist/slices/model-checks/rules.js +112 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/switch.js +70 -25
- package/dist/slices/model-upkeep/usage.js +26 -11
- package/dist/slices/narration/breaths.js +2 -1
- package/dist/slices/narration/clean-pieces.js +2 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +3 -2
- package/dist/slices/narration/steering.js +4 -3
- package/dist/slices/notifications/notifier.js +4 -3
- package/dist/slices/notifications/rules.js +5 -2
- package/dist/slices/notifications/settings.js +4 -12
- package/dist/slices/notifications/subject.js +2 -1
- package/dist/slices/onboarding/copy.js +2 -1
- package/dist/slices/onboarding/first-run.js +3 -2
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +4 -3
- package/dist/slices/onboarding/model.js +0 -16
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/quick-short.js +2 -1
- package/dist/slices/onboarding/sample.js +0 -1
- package/dist/slices/onboarding/state.js +2 -11
- package/dist/slices/onboarding/template.js +2 -1
- package/dist/slices/patch-notes/library.js +3 -2
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/review-inputs.js +8 -2
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup.js +8 -0
- package/dist/slices/project-templates/stand-ins.js +22 -0
- package/dist/slices/rebuild/activity.js +2 -0
- package/dist/slices/rebuild/admission-repo.js +3 -2
- package/dist/slices/rebuild/narration-history.js +4 -5
- package/dist/slices/rebuild/narration-retry.js +2 -1
- package/dist/slices/rebuild/preview-details.js +27 -9
- package/dist/slices/rebuild/preview-plan.js +3 -8
- package/dist/slices/rebuild/recipe-build.js +8 -3
- package/dist/slices/rebuild/recipe-describe.js +3 -3
- package/dist/slices/rebuild/recipe-digest.js +58 -0
- package/dist/slices/rebuild/recipe-input-schema.js +10 -0
- package/dist/slices/rebuild/recipe-keywords.js +71 -0
- package/dist/slices/rebuild/recipe-legacy.js +5 -1
- package/dist/slices/rebuild/recipe-model.js +4 -0
- package/dist/slices/rebuild/recipe-narration-text.js +2 -1
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-reference.js +8 -4
- package/dist/slices/rebuild/recipe-reviews.js +27 -7
- package/dist/slices/rebuild/recipe-save.js +6 -3
- package/dist/slices/rebuild/recipe-scenes.js +40 -11
- package/dist/slices/rebuild/recipe-shorts.js +8 -8
- package/dist/slices/rebuild/recipe-social.js +46 -0
- package/dist/slices/rebuild/recipe-text.js +64 -20
- package/dist/slices/rebuild/recipe-validation.js +98 -28
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-work.js +15 -3
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +3 -2
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +2 -1
- package/dist/slices/rebuild/runtime-admission.js +4 -3
- package/dist/slices/rebuild/runtime-agent-review.js +11 -5
- package/dist/slices/rebuild/runtime-animate.js +9 -8
- package/dist/slices/rebuild/runtime-article.js +3 -2
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +8 -11
- package/dist/slices/rebuild/runtime-export-bed.js +6 -11
- package/dist/slices/rebuild/runtime-export-edit.js +6 -15
- package/dist/slices/rebuild/runtime-export-inputs.js +7 -7
- package/dist/slices/rebuild/runtime-export-short.js +8 -4
- package/dist/slices/rebuild/runtime-export.js +34 -12
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +4 -9
- package/dist/slices/rebuild/runtime-instructions.js +2 -0
- package/dist/slices/rebuild/runtime-keywords.js +59 -0
- package/dist/slices/rebuild/runtime-lines.js +2 -11
- package/dist/slices/rebuild/runtime-local.js +14 -20
- package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
- package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
- package/dist/slices/rebuild/runtime-narration-text.js +10 -15
- package/dist/slices/rebuild/runtime-pictures.js +2 -4
- package/dist/slices/rebuild/runtime-piece-label.js +10 -6
- package/dist/slices/rebuild/runtime-plan.js +31 -5
- package/dist/slices/rebuild/runtime-provider.js +60 -19
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +33 -28
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +34 -50
- package/dist/slices/rebuild/runtime-store.js +3 -8
- package/dist/slices/rebuild/runtime-subtitles.js +16 -14
- package/dist/slices/rebuild/runtime-voices.js +8 -15
- package/dist/slices/rebuild/runtime-youtube.js +11 -29
- package/dist/slices/rebuild/transition-repo.js +6 -5
- package/dist/slices/rebuild/work-records.js +6 -0
- package/dist/slices/reruns/cascade.js +1 -2
- package/dist/slices/reruns/index.js +6 -9
- package/dist/slices/reviews/external-model.js +3 -0
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/model.js +26 -8
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +1 -0
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/mutation-assets.js +7 -4
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +0 -1
- package/dist/slices/revisions/repo.js +3 -1
- package/dist/slices/revisions/schema.js +1 -0
- package/dist/slices/revisions/view.js +20 -0
- package/dist/slices/run-cost/limits.js +5 -2
- package/dist/slices/run-cost/meter.js +12 -5
- package/dist/slices/run-cost/panel.js +8 -74
- package/dist/slices/run-cost/plan-readings.js +127 -0
- package/dist/slices/run-cost/plan-use.js +169 -0
- package/dist/slices/run-cost/pricing.js +3 -3
- package/dist/slices/run-cost/week.js +32 -4
- package/dist/slices/schedules/agenda.js +11 -2
- package/dist/slices/schedules/by-date.js +42 -0
- package/dist/slices/schedules/calendar.js +5 -0
- package/dist/slices/schedules/design-agenda.js +36 -0
- package/dist/slices/schedules/design-check.js +41 -0
- package/dist/slices/schedules/design-run.js +126 -0
- package/dist/slices/schedules/next-run.js +62 -0
- package/dist/slices/schedules/prepare.js +18 -2
- package/dist/slices/schedules/repo.js +24 -16
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +54 -3
- package/dist/slices/schedules/score.js +107 -0
- package/dist/slices/schedules/service.js +57 -24
- package/dist/slices/schedules/topic-list.js +15 -0
- package/dist/slices/schedules/topic-scores.js +104 -0
- package/dist/slices/schedules/topics.js +48 -36
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +2 -11
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +10 -0
- package/dist/slices/settings/playback.js +33 -22
- package/dist/slices/settings/probe-read.js +16 -0
- package/dist/slices/settings/repo.js +33 -0
- package/dist/slices/settings/tutorial.js +2 -2
- package/dist/slices/settings/voices.js +4 -12
- package/dist/slices/shorts/pick.js +2 -15
- package/dist/slices/shorts/prompts.js +2 -13
- package/dist/slices/shorts/render.js +3 -0
- package/dist/slices/social/channel.js +44 -0
- package/dist/slices/social/defaults.js +17 -0
- package/dist/slices/social/due.js +36 -0
- package/dist/slices/social/extension.js +18 -0
- package/dist/slices/social/fit.js +115 -0
- package/dist/slices/social/limits.js +186 -0
- package/dist/slices/social/links.js +21 -0
- package/dist/slices/social/media.js +131 -0
- package/dist/slices/social/model.js +75 -0
- package/dist/slices/social/notice.js +19 -0
- package/dist/slices/social/pack.js +176 -0
- package/dist/slices/social/post-prompt.js +55 -0
- package/dist/slices/social/queue.js +31 -0
- package/dist/slices/social/repo.js +133 -0
- package/dist/slices/social/reshape.js +97 -0
- package/dist/slices/social/rules.js +5 -0
- package/dist/slices/social/schema.js +70 -0
- package/dist/slices/social/serve.js +51 -0
- package/dist/slices/social/service.js +128 -0
- package/dist/slices/social/settings.js +21 -0
- package/dist/slices/social/sources.js +114 -0
- package/dist/slices/social/state.js +110 -0
- package/dist/slices/social/text.js +149 -0
- package/dist/slices/storage/arrange.js +3 -1
- package/dist/slices/storage/assets.js +8 -10
- package/dist/slices/storage/backup-designs.js +74 -0
- package/dist/slices/storage/backup-export.js +8 -3
- package/dist/slices/storage/backup-format.js +10 -0
- package/dist/slices/storage/backup-import.js +20 -2
- package/dist/slices/storage/delete-project.js +6 -5
- package/dist/slices/storage/files-location.js +6 -8
- package/dist/slices/storage/layout.js +13 -2
- package/dist/slices/storage/model.js +3 -0
- package/dist/slices/storage/portable.js +16 -11
- package/dist/slices/storage/schema.js +2 -0
- package/dist/slices/storage/staging-refs.js +2 -1
- package/dist/slices/storage/staging.js +6 -9
- package/dist/slices/storage/trim.js +10 -3
- package/dist/slices/studio/calendar.js +4 -1
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +6 -15
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +14 -51
- package/dist/slices/studio/videos.js +12 -0
- package/dist/slices/style-preview/render.js +34 -13
- package/dist/slices/style-preview/schema.js +13 -0
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/telemetry/collector-client.js +2 -4
- package/dist/slices/telemetry/flush.js +2 -4
- package/dist/slices/telemetry/model.js +0 -7
- package/dist/slices/telemetry/record.js +10 -3
- package/dist/slices/template-studio/anatomy-view.js +118 -0
- package/dist/slices/template-studio/anatomy.js +73 -0
- package/dist/slices/template-studio/bakeoff-make.js +225 -0
- package/dist/slices/template-studio/bakeoff-repo.js +83 -0
- package/dist/slices/template-studio/bakeoff.js +333 -0
- package/dist/slices/template-studio/budget.js +103 -0
- package/dist/slices/template-studio/checks.js +273 -0
- package/dist/slices/template-studio/document-sample.js +56 -0
- package/dist/slices/template-studio/edit.js +15 -0
- package/dist/slices/template-studio/for-all.js +161 -0
- package/dist/slices/template-studio/free-video.js +31 -0
- package/dist/slices/template-studio/free.js +176 -0
- package/dist/slices/template-studio/gate.js +40 -0
- package/dist/slices/template-studio/hidden.js +20 -0
- package/dist/slices/template-studio/jobs.js +79 -0
- package/dist/slices/template-studio/loudness.js +111 -0
- package/dist/slices/template-studio/make.js +40 -0
- package/dist/slices/template-studio/model.js +54 -0
- package/dist/slices/template-studio/pack-import.js +253 -0
- package/dist/slices/template-studio/pack-samples.js +60 -0
- package/dist/slices/template-studio/pack.js +205 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +348 -0
- package/dist/slices/template-studio/quote.js +148 -0
- package/dist/slices/template-studio/ready.js +104 -0
- package/dist/slices/template-studio/repo.js +134 -0
- package/dist/slices/template-studio/reviews-path.js +58 -0
- package/dist/slices/template-studio/runner.js +84 -0
- package/dist/slices/template-studio/save.js +236 -0
- package/dist/slices/template-studio/say-it-as.js +248 -0
- package/dist/slices/template-studio/schedule-gate.js +34 -0
- package/dist/slices/template-studio/schedule-price.js +30 -0
- package/dist/slices/template-studio/schedule-sample.js +87 -0
- package/dist/slices/template-studio/schema.js +218 -0
- package/dist/slices/template-studio/scope.js +124 -0
- package/dist/slices/template-studio/sections.js +176 -0
- package/dist/slices/template-studio/service.js +173 -0
- package/dist/slices/template-studio/status.js +174 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/trash/designs.js +76 -0
- package/dist/slices/trash/model.js +13 -4
- package/dist/slices/trash/service.js +52 -2
- package/dist/slices/tutorials/library.js +3 -2
- package/dist/slices/video/ass-text.js +17 -0
- package/dist/slices/video/audio-export.js +2 -64
- package/dist/slices/video/encoding.js +160 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/youtube/answer.js +2 -15
- package/dist/slices/youtube/edits-repo.js +2 -11
- package/dist/tutorials/Agents-MCP.md +2 -1
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Channels.md +2 -0
- package/dist/tutorials/Home-and-Projects.md +19 -0
- package/dist/tutorials/Install.md +1 -0
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +33 -1
- package/dist/tutorials/Notifications.md +6 -0
- package/dist/tutorials/Play-Images.md +4 -0
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Prompts.md +10 -6
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +13 -2
- package/dist/tutorials/Schedules.md +21 -1
- package/dist/tutorials/Settings-Reference.md +50 -8
- package/dist/tutorials/Template-Studio.md +125 -0
- package/dist/tutorials/Templates.md +3 -0
- package/dist/tutorials/_Sidebar.md +5 -0
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-CkAI_2E7.css +1 -0
- package/dist/web/assets/index-DcD0cdfo.js +334 -0
- package/dist/web/assets/{pdf-T7MCwjIv.js → pdf-TODRwF31.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -1
- package/dist/slices/article/provided-entries.js +0 -66
- package/dist/slices/article/run.js +0 -145
- package/dist/slices/images/run.js +0 -234
- package/dist/slices/narration/legacy-plan.js +0 -58
- package/dist/slices/narration/run.js +0 -270
- package/dist/slices/rebuild/runtime-actions.js +0 -90
- package/dist/slices/research/run.js +0 -224
- package/dist/slices/subtitles/prepare.js +0 -194
- package/dist/slices/subtitles/transcript.js +0 -34
- package/dist/slices/thumbnail/run.js +0 -219
- package/dist/slices/video/audio-inputs.js +0 -33
- package/dist/slices/video/reuse-audio.js +0 -62
- package/dist/slices/video/run.js +0 -78
- package/dist/slices/video/write-export.js +0 -158
- package/dist/slices/video/write-subtitles.js +0 -59
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
|
@@ -1,30 +1,79 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { jsonBetween } from "../../kernel/json-answer.js";
|
|
2
3
|
import { detectSlots, render, sceneKeyword } from "../admission/substitute.js";
|
|
3
4
|
// Scenes from the article (Images → Scenes from the article): each image is drawn from its
|
|
4
5
|
// own scene instead of the same text every time. One call to the project's AI model reads the
|
|
5
6
|
// article and writes every scene, following the article from its opening to its end in the
|
|
6
7
|
// order the images are shown.
|
|
8
|
+
// The keywords Slopify fills per picture from the scenes call (`keywords/specs.ts`): any of them
|
|
9
|
+
// turns scenes on for that picture, whatever the Images switch says.
|
|
10
|
+
export const slopifySceneKeywords = ["Slopify_Scene", "Slopify_Setting", "Slopify_Mood"];
|
|
11
|
+
const sceneNames = [sceneKeyword, "Slopify_Scene"];
|
|
12
|
+
const detailNames = ["Slopify_Setting", "Slopify_Mood"];
|
|
13
|
+
// `{{Scene}}` or `{{Slopify_Scene}}`: where the scene goes.
|
|
7
14
|
export function usesScene(body) {
|
|
15
|
+
return body != null && detectSlots(body).names.some((name) => sceneNames.includes(name));
|
|
16
|
+
}
|
|
17
|
+
// The old name, which follows Images → Scenes from the article as it always did.
|
|
18
|
+
export function usesLegacyScene(body) {
|
|
8
19
|
return body != null && detectSlots(body).names.includes(sceneKeyword);
|
|
9
20
|
}
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
// `{{Slopify_Scene}}`, `{{Slopify_Setting}}` or `{{Slopify_Mood}}`: the picture gets its scene.
|
|
22
|
+
export function usesSlopifyScene(body) {
|
|
23
|
+
return (body != null &&
|
|
24
|
+
detectSlots(body).names.some((name) => slopifySceneKeywords.includes(name)));
|
|
25
|
+
}
|
|
26
|
+
// `{{Slopify_Setting}}` or `{{Slopify_Mood}}`: the scenes call writes them too.
|
|
27
|
+
export function wantsSceneDetails(body) {
|
|
28
|
+
return body != null && detectSlots(body).names.some((name) => detailNames.includes(name));
|
|
29
|
+
}
|
|
30
|
+
// The prompt with its scene: where the prompt has `{{Scene}}` or `{{Slopify_Scene}}`, there;
|
|
31
|
+
// otherwise, while the Images switch is on (`legacy`), as a line of its own after the prompt's
|
|
32
|
+
// first paragraph, so any prompt takes a scene without editing. With the switch off a picture is
|
|
33
|
+
// here only for its `Slopify_` keywords, and a `{{Scene}}` line is left out as it always was.
|
|
34
|
+
// The setting and mood go where the prompt asks; a line asking for one not written is left out.
|
|
35
|
+
export function withScene(body, scene, options = {}) {
|
|
36
|
+
const legacy = options.legacy !== false;
|
|
37
|
+
const text = legacy ? body : withoutLines(body, [sceneKeyword]);
|
|
38
|
+
const detailed = withDetails(text, options);
|
|
39
|
+
if (usesScene(detailed))
|
|
40
|
+
return render(detailed, { [sceneKeyword]: scene, Slopify_Scene: scene });
|
|
41
|
+
if (!legacy)
|
|
42
|
+
return detailed;
|
|
43
|
+
const at = detailed.indexOf("\n\n");
|
|
16
44
|
return at === -1
|
|
17
|
-
? `${
|
|
18
|
-
: `${
|
|
45
|
+
? `${detailed}\n\nScene: ${scene}`
|
|
46
|
+
: `${detailed.slice(0, at)}\n\nScene: ${scene}${detailed.slice(at)}`;
|
|
19
47
|
}
|
|
20
|
-
//
|
|
21
|
-
//
|
|
48
|
+
// A picture that is drawn without scenes (the establishing image): a line holding a `Slopify_`
|
|
49
|
+
// scene keyword is left out, and `{{Scene}}` stays as it always did.
|
|
50
|
+
export function withoutSlopifyScene(body) {
|
|
51
|
+
return withoutLines(body, slopifySceneKeywords);
|
|
52
|
+
}
|
|
53
|
+
// The prompt with no scene: a line that holds a scene keyword is left out, so the image is drawn
|
|
54
|
+
// from the rest of the prompt as it always was.
|
|
22
55
|
export function withoutScene(body) {
|
|
23
|
-
|
|
56
|
+
return withoutLines(body, [...sceneNames, ...detailNames]);
|
|
57
|
+
}
|
|
58
|
+
function withDetails(body, details) {
|
|
59
|
+
if (!wantsSceneDetails(body))
|
|
60
|
+
return body;
|
|
61
|
+
const missing = [
|
|
62
|
+
...(details.setting?.trim() ? [] : ["Slopify_Setting"]),
|
|
63
|
+
...(details.mood?.trim() ? [] : ["Slopify_Mood"]),
|
|
64
|
+
];
|
|
65
|
+
return render(withoutLines(body, missing), {
|
|
66
|
+
...(details.setting?.trim() ? { Slopify_Setting: details.setting.trim() } : {}),
|
|
67
|
+
...(details.mood?.trim() ? { Slopify_Mood: details.mood.trim() } : {}),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function withoutLines(body, names) {
|
|
71
|
+
const holds = (line) => detectSlots(line).names.some((name) => names.includes(name));
|
|
72
|
+
if (!body.split("\n").some(holds))
|
|
24
73
|
return body;
|
|
25
74
|
return body
|
|
26
75
|
.split("\n")
|
|
27
|
-
.filter((line) => !
|
|
76
|
+
.filter((line) => !holds(line))
|
|
28
77
|
.join("\n")
|
|
29
78
|
.replace(/\n{3,}/g, "\n\n")
|
|
30
79
|
.trim();
|
|
@@ -42,7 +91,25 @@ const countLine = /^Pictures: (\d+)$/m;
|
|
|
42
91
|
// prompt asks for one with `{{Scene}}`. They are not tied to a stretch of the article: each is
|
|
43
92
|
// the moment that would make someone click, a different one for each thumbnail.
|
|
44
93
|
const thumbnailLine = /^Thumbnails: (\d+)$/m;
|
|
94
|
+
// With `details`, each scene comes with its setting and mood for `{{Slopify_Setting}}` and
|
|
95
|
+
// `{{Slopify_Mood}}`; without, the request is the one it always was.
|
|
45
96
|
export function sceneMessages(brief) {
|
|
97
|
+
const plain = plainSceneMessages(brief);
|
|
98
|
+
return brief.details === true ? plain.map(askingDetails) : plain;
|
|
99
|
+
}
|
|
100
|
+
const answerLine = /^Answer with a JSON array of exactly (\d+) strings, (.*), and nothing else\.$/m;
|
|
101
|
+
function askingDetails(message) {
|
|
102
|
+
if (message.role !== "system")
|
|
103
|
+
return message;
|
|
104
|
+
return {
|
|
105
|
+
...message,
|
|
106
|
+
content: message.content.replace(answerLine, (_whole, total, order) => [
|
|
107
|
+
"For each scene also write its setting: the era and place of that moment in a few words (period, region, season), so the picture stays true to its time; and its mood: the feeling of that moment in one to three words (calm, tense, sad), for the picture's light and colour.",
|
|
108
|
+
`Answer with a JSON array of exactly ${total} objects, ${order}, each {"scene": "…", "setting": "…", "mood": "…"}, and nothing else.`,
|
|
109
|
+
].join("\n\n")),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function plainSceneMessages(brief) {
|
|
46
113
|
const count = brief.pictures.length;
|
|
47
114
|
const thumbnails = brief.thumbnails ?? 0;
|
|
48
115
|
if (thumbnails > 0)
|
|
@@ -120,15 +187,33 @@ function withThumbnails(brief, count, thumbnails) {
|
|
|
120
187
|
},
|
|
121
188
|
];
|
|
122
189
|
}
|
|
190
|
+
const sceneAnswer = z.array(z.union([
|
|
191
|
+
z.string(),
|
|
192
|
+
z.object({
|
|
193
|
+
scene: z.string(),
|
|
194
|
+
setting: z.string().optional(),
|
|
195
|
+
mood: z.string().optional(),
|
|
196
|
+
}),
|
|
197
|
+
]));
|
|
198
|
+
// A list of scenes, or of scenes with their setting and mood (`details`, only then present).
|
|
123
199
|
export function checkScenes(text, count) {
|
|
124
200
|
const fix = "Use Try again; if it keeps happening, choose another model in the Providers section of Edit project.";
|
|
125
|
-
const parsed =
|
|
201
|
+
const parsed = sceneAnswer.safeParse(jsonBetween(text, "[", "]"));
|
|
126
202
|
if (!parsed.success)
|
|
127
203
|
return {
|
|
128
204
|
ok: false,
|
|
129
205
|
reason: `The AI model didn't answer with a list of image scenes. ${fix}`,
|
|
130
206
|
};
|
|
131
|
-
const
|
|
207
|
+
const tidy = (value) => value.replace(/\s+/g, " ").trim();
|
|
208
|
+
const scenes = parsed.data.map((one) => tidy(typeof one === "string" ? one : one.scene));
|
|
209
|
+
const details = parsed.data.some((one) => typeof one !== "string")
|
|
210
|
+
? parsed.data.map((one) => typeof one === "string"
|
|
211
|
+
? {}
|
|
212
|
+
: {
|
|
213
|
+
...(one.setting === undefined ? {} : { setting: tidy(one.setting) }),
|
|
214
|
+
...(one.mood === undefined ? {} : { mood: tidy(one.mood) }),
|
|
215
|
+
})
|
|
216
|
+
: undefined;
|
|
132
217
|
if (scenes.length !== count)
|
|
133
218
|
return {
|
|
134
219
|
ok: false,
|
|
@@ -136,17 +221,5 @@ export function checkScenes(text, count) {
|
|
|
136
221
|
};
|
|
137
222
|
if (scenes.some((scene) => scene === ""))
|
|
138
223
|
return { ok: false, reason: `One of the AI model's image scenes is empty. ${fix}` };
|
|
139
|
-
return { ok: true, scenes };
|
|
140
|
-
}
|
|
141
|
-
function jsonArrayOf(text) {
|
|
142
|
-
const start = text.indexOf("[");
|
|
143
|
-
const end = text.lastIndexOf("]");
|
|
144
|
-
if (start === -1 || end <= start)
|
|
145
|
-
return undefined;
|
|
146
|
-
try {
|
|
147
|
-
return JSON.parse(text.slice(start, end + 1));
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return undefined;
|
|
151
|
-
}
|
|
224
|
+
return { ok: true, scenes, ...(details === undefined ? {} : { details }) };
|
|
152
225
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { keywordSpecs } from "./specs.js";
|
|
3
|
+
// The keyword digest: one call to the project's AI model reads the article once and writes every
|
|
4
|
+
// digest keyword (`{{Slopify_Summary}}`, `{{Slopify_Hook}}`…), so a run that uses several pays for
|
|
5
|
+
// one call. It asks for all six every time, so adding one more digest keyword to a prompt later
|
|
6
|
+
// reuses the same answer.
|
|
7
|
+
export const digestNames = keywordSpecs
|
|
8
|
+
.filter((spec) => spec.filler === "digest")
|
|
9
|
+
.map((spec) => spec.name);
|
|
10
|
+
export function digestMessages(brief) {
|
|
11
|
+
return [
|
|
12
|
+
{
|
|
13
|
+
role: "system",
|
|
14
|
+
content: [
|
|
15
|
+
"You write short texts about a narrated video from its article, for its pictures, thumbnail, description, shorts and reviews.",
|
|
16
|
+
"summary: two or three sentences on what the video covers.",
|
|
17
|
+
"hook: the single most click-worthy idea of the video, in one sentence.",
|
|
18
|
+
"thumbnailText: two to four words to put on the thumbnail, in the video's language.",
|
|
19
|
+
"keyFacts: three to five striking facts the article states, each one sentence.",
|
|
20
|
+
"quote: one quotable line from the article, as it is written there.",
|
|
21
|
+
"tags: five to fifteen search terms someone would type to find this video.",
|
|
22
|
+
"Use only what the article says. Write in the article's language.",
|
|
23
|
+
'Answer with JSON and nothing else: {"summary": "…", "hook": "…", "thumbnailText": "…", "keyFacts": ["…"], "quote": "…", "tags": ["…"]}.',
|
|
24
|
+
].join("\n\n"),
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
role: "user",
|
|
28
|
+
content: [`Video: ${brief.title}`, `Article:\n\n${brief.article}`].join("\n\n"),
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
const listOrText = z.union([z.array(z.string()), z.string()]);
|
|
33
|
+
const digestSchema = z.object({
|
|
34
|
+
summary: z.string(),
|
|
35
|
+
hook: z.string(),
|
|
36
|
+
thumbnailText: z.string(),
|
|
37
|
+
keyFacts: listOrText,
|
|
38
|
+
quote: z.string(),
|
|
39
|
+
tags: listOrText,
|
|
40
|
+
});
|
|
41
|
+
export function checkDigest(text) {
|
|
42
|
+
const fix = "Use Try again; if it keeps happening, choose another model in the Providers section of Edit project.";
|
|
43
|
+
const parsed = digestSchema.safeParse(jsonObjectOf(text));
|
|
44
|
+
if (!parsed.success)
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
reason: `The AI model didn't answer with the keyword digest (summary, hook, thumbnail text, key facts, quote and tags). ${fix}`,
|
|
48
|
+
};
|
|
49
|
+
const tidy = (value) => value.replace(/\s+/g, " ").trim();
|
|
50
|
+
const list = (value) => (typeof value === "string" ? value.split("\n") : value)
|
|
51
|
+
.map((one) => tidy(one).replace(/^[-•*]\s*/, ""))
|
|
52
|
+
.filter((one) => one !== "");
|
|
53
|
+
const { summary, hook, thumbnailText, keyFacts, quote, tags } = parsed.data;
|
|
54
|
+
const digest = {
|
|
55
|
+
Slopify_Summary: tidy(summary),
|
|
56
|
+
Slopify_Hook: tidy(hook),
|
|
57
|
+
Slopify_Thumbnail_Text: tidy(thumbnailText),
|
|
58
|
+
Slopify_Key_Facts: list(keyFacts).join("\n"),
|
|
59
|
+
Slopify_Quote: tidy(quote),
|
|
60
|
+
Slopify_Tags: (typeof tags === "string" ? tags.split(",") : tags)
|
|
61
|
+
.map(tidy)
|
|
62
|
+
.filter((one) => one !== "")
|
|
63
|
+
.join(", "),
|
|
64
|
+
};
|
|
65
|
+
const empty = digestNames.filter((name) => (digest[name] ?? "") === "");
|
|
66
|
+
if (empty.length > 0)
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
reason: `The AI model left part of the keyword digest empty (${empty.map((name) => `{{${name}}}`).join(", ")}). ${fix}`,
|
|
70
|
+
};
|
|
71
|
+
return { ok: true, digest };
|
|
72
|
+
}
|
|
73
|
+
function jsonObjectOf(text) {
|
|
74
|
+
const start = text.indexOf("{");
|
|
75
|
+
const end = text.lastIndexOf("}");
|
|
76
|
+
if (start === -1 || end <= start)
|
|
77
|
+
return undefined;
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(text.slice(start, end + 1));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// The facts a run copies when it starts, for the keywords Slopify fills from them: what the
|
|
2
|
+
// channel, its links, its cast, its brand kit and its other projects are now. Later edits there
|
|
3
|
+
// never change a project already made; Edit project's Refresh keyword facts copies them again.
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { listProjects } from "../admission/repo.js";
|
|
7
|
+
import { castOfChannel, channelById, projectChannels, resolveChannelId } from "../channels/repo.js";
|
|
8
|
+
import { readFontMetadata } from "../fonts/sfnt.js";
|
|
9
|
+
import { seriesOf } from "../studio/plan-model.js";
|
|
10
|
+
import { allReleases } from "../studio/releases.js";
|
|
11
|
+
import { videoOf } from "../studio/videos.js";
|
|
12
|
+
import { readChannelLinksFor } from "../youtube/edits-repo.js";
|
|
13
|
+
// ceiling: the titles a prompt is told about. A channel with hundreds of videos would fill the
|
|
14
|
+
// prompt with titles; the newest are the ones it is likeliest to repeat.
|
|
15
|
+
export const coveredBeforeMax = 50;
|
|
16
|
+
// `projectId` is the project the facts are for, when it exists already (a refresh): it is
|
|
17
|
+
// neither its own previous video nor one it covered before.
|
|
18
|
+
export function keywordFactsFor(deps, draft, now, projectId) {
|
|
19
|
+
const { db } = deps;
|
|
20
|
+
const channelId = resolveChannelId(db, draft.channelId);
|
|
21
|
+
const channel = channelById(db, channelId);
|
|
22
|
+
const channels = projectChannels(db);
|
|
23
|
+
const projects = listProjects(db);
|
|
24
|
+
// A refresh sees the channel as it was before this project, never the episodes made after it.
|
|
25
|
+
const made = projects.find((project) => project.id === projectId)?.createdAt;
|
|
26
|
+
const own = projects.filter((project) => project.id !== projectId &&
|
|
27
|
+
(made === undefined || project.createdAt < made) &&
|
|
28
|
+
(channels.get(project.id) ?? channelId) === channelId);
|
|
29
|
+
const series = seriesOf(draft);
|
|
30
|
+
const sameSeries = own.filter((project) => seriesOf(project.config) === series);
|
|
31
|
+
const later = projects.filter((project) => project.id !== projectId &&
|
|
32
|
+
seriesOf(project.config) === series &&
|
|
33
|
+
(channels.get(project.id) ?? channelId) === channelId);
|
|
34
|
+
const previous = sameSeries[0];
|
|
35
|
+
const previousVideo = previous === undefined ? undefined : videoOf(db, previous.id, null)?.videoId;
|
|
36
|
+
const upcoming = allReleases(db).find((release) => release.short === 0 &&
|
|
37
|
+
Date.parse(release.at) > now.getTime() &&
|
|
38
|
+
later.some((project) => project.id === release.projectId));
|
|
39
|
+
const next = upcoming === undefined ? undefined : later.find((one) => one.id === upcoming.projectId);
|
|
40
|
+
const links = readChannelLinksFor(db, channelId);
|
|
41
|
+
const cast = castOfChannel(db, channelId);
|
|
42
|
+
const brand = draft.useBrandKit === false ? undefined : channel?.brand;
|
|
43
|
+
const colours = unique([brand?.captionColor, brand?.captionOutlineColor, brand?.titleColor]);
|
|
44
|
+
const fonts = unique([brand?.captionFontId, brand?.titleFontId].map((id) => id === undefined ? undefined : fontName(deps.paths, id)));
|
|
45
|
+
return {
|
|
46
|
+
...(channel === undefined ? {} : { channel: channel.name }),
|
|
47
|
+
...(channel?.seriesBrief.trim() ? { series: channel.seriesBrief.trim() } : {}),
|
|
48
|
+
...(links.length === 0 ? {} : { links: links.map(({ name, url }) => ({ name, url })) }),
|
|
49
|
+
...(previous === undefined
|
|
50
|
+
? {}
|
|
51
|
+
: {
|
|
52
|
+
previousVideo: {
|
|
53
|
+
title: previous.title,
|
|
54
|
+
...(previousVideo === undefined ? {} : { url: `https://youtu.be/${previousVideo}` }),
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
...(next === undefined ? {} : { nextVideo: { title: next.title } }),
|
|
58
|
+
...(cast.length === 0
|
|
59
|
+
? {}
|
|
60
|
+
: { cast: cast.map(({ name, description }) => ({ name, description })) }),
|
|
61
|
+
...(own.length === 0
|
|
62
|
+
? {}
|
|
63
|
+
: { coveredBefore: own.slice(0, coveredBeforeMax).map((project) => project.title) }),
|
|
64
|
+
episode: sameSeries.length + 1,
|
|
65
|
+
...(colours.length === 0 && fonts.length === 0 ? {} : { brand: { colours, fonts } }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// A brand-kit font's name: the bundled one's, or an uploaded font's own. A system font is
|
|
69
|
+
// found only by scanning the computer's font folders, which a run's start never waits for, so
|
|
70
|
+
// it is left out.
|
|
71
|
+
function fontName(paths, id) {
|
|
72
|
+
if (id === "default")
|
|
73
|
+
return "Barlow";
|
|
74
|
+
if (!/^uploaded-[a-f0-9]{64}$/.test(id))
|
|
75
|
+
return undefined;
|
|
76
|
+
for (const extension of [".ttf", ".otf"]) {
|
|
77
|
+
let bytes;
|
|
78
|
+
try {
|
|
79
|
+
bytes = readFileSync(join(paths.dataDir, "fonts", `${id}${extension}`));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
83
|
+
continue;
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
return readFontMetadata(bytes)?.[0]?.family;
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
function unique(values) {
|
|
91
|
+
return [...new Set(values.filter((value) => value !== undefined))];
|
|
92
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Filling the keywords Slopify fills into a prompt that `render` has already filled with the
|
|
2
|
+
// person's own values, so a value they gave a `Slopify_` name still wins. A body with no
|
|
3
|
+
// keyword comes back as the very same string. Browser-safe.
|
|
4
|
+
import { keywordOf } from "./specs.js";
|
|
5
|
+
const slot = /\{\{([^{}\n]*)\}\}/g;
|
|
6
|
+
// Filled elsewhere: each picture's own keywords by the pictures, the release date and the length
|
|
7
|
+
// when the step runs.
|
|
8
|
+
const elsewhere = ["scenes", "appearance", "release", "duration"];
|
|
9
|
+
export function mentionsKeyword(body) {
|
|
10
|
+
for (const match of body.matchAll(slot))
|
|
11
|
+
if (keywordOf((match[1] ?? "").trim())?.name === (match[1] ?? "").trim())
|
|
12
|
+
return true;
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
export function fillKeywords(body, place, values) {
|
|
16
|
+
return fillWith(body, (name) => {
|
|
17
|
+
const spec = keywordOf(name);
|
|
18
|
+
if (spec === undefined || spec.name !== name || !spec.places.includes(place))
|
|
19
|
+
return undefined;
|
|
20
|
+
if (elsewhere.includes(spec.filler))
|
|
21
|
+
return undefined;
|
|
22
|
+
return Object.hasOwn(values, name) ? (values[name] ?? "") : null;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
// The release date and the length, filled when the step runs: they are left in the planned
|
|
26
|
+
// request, so a later change to either never makes anything outdated.
|
|
27
|
+
export function fillLateKeywords(text, late) {
|
|
28
|
+
const filled = fillWith(text, (name) => name === "Slopify_Release_Date"
|
|
29
|
+
? (late.releaseDate ?? "")
|
|
30
|
+
: name === "Slopify_Duration"
|
|
31
|
+
? (late.duration ?? "")
|
|
32
|
+
: undefined);
|
|
33
|
+
return filled.ok ? filled.text : text;
|
|
34
|
+
}
|
|
35
|
+
export function mentionsLateKeyword(text) {
|
|
36
|
+
return text.includes("Slopify_Release_Date") || text.includes("Slopify_Duration");
|
|
37
|
+
}
|
|
38
|
+
// `valueFor` answers a string to fill (an empty one leaves its line out), null for a keyword
|
|
39
|
+
// still waiting, and undefined for a name to leave as written.
|
|
40
|
+
export function fillWith(body, valueFor) {
|
|
41
|
+
const waiting = [];
|
|
42
|
+
const blank = [];
|
|
43
|
+
let touched = false;
|
|
44
|
+
for (const match of body.matchAll(slot)) {
|
|
45
|
+
const name = (match[1] ?? "").trim();
|
|
46
|
+
const value = valueFor(name);
|
|
47
|
+
if (value === undefined)
|
|
48
|
+
continue;
|
|
49
|
+
touched = true;
|
|
50
|
+
if (value === null && !waiting.includes(name))
|
|
51
|
+
waiting.push(name);
|
|
52
|
+
if (value === "" && !blank.includes(name))
|
|
53
|
+
blank.push(name);
|
|
54
|
+
}
|
|
55
|
+
if (!touched)
|
|
56
|
+
return { ok: true, text: body };
|
|
57
|
+
if (waiting.length > 0)
|
|
58
|
+
return { ok: false, waiting };
|
|
59
|
+
const kept = blank.length === 0
|
|
60
|
+
? body
|
|
61
|
+
: body
|
|
62
|
+
.split("\n")
|
|
63
|
+
.filter((line) => !blank.some((name) => holds(line, name)))
|
|
64
|
+
.join("\n")
|
|
65
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
66
|
+
.trim();
|
|
67
|
+
return {
|
|
68
|
+
ok: true,
|
|
69
|
+
text: kept.replace(slot, (whole, inside) => {
|
|
70
|
+
const value = valueFor(inside.trim());
|
|
71
|
+
return value === undefined || value === null ? whole : value;
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function holds(line, name) {
|
|
76
|
+
for (const match of line.matchAll(slot))
|
|
77
|
+
if ((match[1] ?? "").trim() === name)
|
|
78
|
+
return true;
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// The keywords Slopify fills itself: never fields for the person, always written with the
|
|
2
|
+
// reserved `Slopify_` prefix so they never take a name a template already uses. One table
|
|
3
|
+
// says, for each, what fills it, where it works and what it costs; the editor's Insert bar,
|
|
4
|
+
// its hover cards, the help tips, the lint and the fillers all read it. Browser-safe: the
|
|
5
|
+
// editor imports it.
|
|
6
|
+
// Where a keyword can appear: the prompt kinds, the intro and outro entries, and the title.
|
|
7
|
+
export const keywordPlaces = [
|
|
8
|
+
"title",
|
|
9
|
+
"article",
|
|
10
|
+
"script",
|
|
11
|
+
"narration",
|
|
12
|
+
"intro",
|
|
13
|
+
"outro",
|
|
14
|
+
"image",
|
|
15
|
+
"thumbnail",
|
|
16
|
+
"description",
|
|
17
|
+
"shorts",
|
|
18
|
+
"review",
|
|
19
|
+
// A Library prompt that writes a post for another site (`slices/social`).
|
|
20
|
+
"post",
|
|
21
|
+
];
|
|
22
|
+
// What fills a keyword: facts copied when the run starts, the release date read when the step
|
|
23
|
+
// runs, the article's chapter titles, the finished length, the shared digest call, the scenes
|
|
24
|
+
// call and the appearance lookup.
|
|
25
|
+
export const fillerKinds = [
|
|
26
|
+
"facts",
|
|
27
|
+
"release",
|
|
28
|
+
"chapters",
|
|
29
|
+
"duration",
|
|
30
|
+
"digest",
|
|
31
|
+
"scenes",
|
|
32
|
+
"appearance",
|
|
33
|
+
];
|
|
34
|
+
// Free, or part of one AI call per run (shared by every keyword with the same cost).
|
|
35
|
+
export const keywordCosts = ["free", "digest", "scenes", "appearance"];
|
|
36
|
+
export const reservedPrefix = "Slopify_";
|
|
37
|
+
// The whole prefix is Slopify's, whatever its case, so a near miss never turns into a field
|
|
38
|
+
// that a later keyword would take over.
|
|
39
|
+
export function isReserved(name) {
|
|
40
|
+
return name.toLowerCase().startsWith(reservedPrefix.toLowerCase());
|
|
41
|
+
}
|
|
42
|
+
// What a keyword's cost says in the Insert bar.
|
|
43
|
+
export function costLabel(cost) {
|
|
44
|
+
switch (cost) {
|
|
45
|
+
case "free":
|
|
46
|
+
return "Free: no AI call.";
|
|
47
|
+
case "digest":
|
|
48
|
+
return "Part of one AI call per run: the keyword digest, shared by every digest keyword.";
|
|
49
|
+
case "scenes":
|
|
50
|
+
return "Part of one AI call per run: the scenes call, shared by every picture.";
|
|
51
|
+
case "appearance":
|
|
52
|
+
return "Part of one AI call per run: the appearance lookup, with web search.";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// How the editor names a place: the prompt kinds and the two entry categories.
|
|
56
|
+
export const placeLabels = {
|
|
57
|
+
title: "Title",
|
|
58
|
+
article: "Article",
|
|
59
|
+
script: "Script",
|
|
60
|
+
narration: "Narration",
|
|
61
|
+
intro: "Intro",
|
|
62
|
+
outro: "Outro",
|
|
63
|
+
image: "Image",
|
|
64
|
+
thumbnail: "Thumbnail",
|
|
65
|
+
description: "Description",
|
|
66
|
+
shorts: "Shorts",
|
|
67
|
+
review: "Review",
|
|
68
|
+
post: "Post",
|
|
69
|
+
};
|
|
70
|
+
// "A, B and C".
|
|
71
|
+
export function listOf(words) {
|
|
72
|
+
if (words.length <= 1)
|
|
73
|
+
return words.join("");
|
|
74
|
+
return `${words.slice(0, -1).join(", ")} and ${words.at(-1) ?? ""}`;
|
|
75
|
+
}
|