@gentbajko/slopify 2.5.0 → 3.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/LICENSE +201 -21
- package/NOTICE +12 -0
- package/README.md +21 -40
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +3 -0
- package/dist/adapters/alignment/cache.js +4 -1
- package/dist/adapters/alignment/ctc.js +25 -23
- package/dist/adapters/alignment/index.js +37 -6
- package/dist/adapters/alignment/multilingual.js +267 -0
- package/dist/adapters/alignment/protocol.js +11 -0
- package/dist/adapters/alignment/quality.js +4 -3
- package/dist/adapters/alignment/sentences.js +95 -0
- package/dist/adapters/alignment/spec.js +24 -0
- package/dist/adapters/alignment/spell.js +769 -0
- package/dist/adapters/alignment/text.js +56 -9
- package/dist/adapters/alignment/window.js +34 -30
- package/dist/adapters/alignment/worker.js +23 -21
- package/dist/adapters/explain.js +11 -11
- package/dist/adapters/host-cli/index.js +44 -5
- package/dist/adapters/host-cli/transport.js +7 -5
- package/dist/adapters/image/bytes.js +2 -2
- package/dist/adapters/image/codex-output.js +1 -1
- package/dist/adapters/image/codex.js +87 -22
- package/dist/adapters/image/fal.js +13 -5
- package/dist/adapters/image/google.js +11 -8
- package/dist/adapters/image/openai.js +14 -7
- package/dist/adapters/image/reference.js +34 -0
- package/dist/adapters/image/replicate.js +7 -2
- package/dist/adapters/image/video.js +2 -2
- package/dist/adapters/key-probes.js +36 -0
- package/dist/adapters/llm/claude-code.js +168 -32
- package/dist/adapters/llm/cli-login-error.js +1 -1
- package/dist/adapters/llm/codex-limits.js +157 -0
- package/dist/adapters/llm/codex.js +99 -12
- package/dist/adapters/llm/document-workspace.js +2 -2
- package/dist/adapters/llm/gemini-workspace.js +1 -1
- package/dist/adapters/llm/gemini.js +16 -3
- package/dist/adapters/llm/image-workspace.js +58 -0
- package/dist/adapters/llm/openrouter.js +11 -3
- package/dist/adapters/llm/run-cli.js +12 -7
- package/dist/adapters/tts/cartesia.js +10 -0
- package/dist/adapters/tts/elevenlabs.js +31 -9
- package/dist/adapters/tts/inworld-async.js +9 -5
- package/dist/adapters/tts/inworld.js +17 -3
- package/dist/adapters/tts/openai.js +3 -0
- package/dist/adapters/tts/voice-languages.js +80 -0
- package/dist/assets/fonts/noto/NotoSans-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansArabic-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansDevanagari-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansHebrew-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/NotoSansThai-Regular.ttf +0 -0
- package/dist/assets/fonts/noto/OFL.txt +93 -0
- package/dist/assets/fonts/noto/SOURCE.txt +18 -0
- package/dist/assets/sample/sample-audiobook.tar +0 -0
- package/dist/assets/sample/sample-podcast.tar +0 -0
- package/dist/assets/sample/sample-project.tar +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +13 -13
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +147 -8
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +50 -0
- package/dist/edge/autostart/index.js +78 -0
- package/dist/edge/autostart/launcher.js +176 -0
- package/dist/edge/autostart/model.js +3 -0
- package/dist/edge/autostart/native.js +210 -0
- package/dist/edge/autostart/prompt.js +66 -0
- package/dist/edge/autostart/service.js +81 -0
- package/dist/edge/cli.js +57 -51
- package/dist/edge/docker-install/activation.js +56 -0
- package/dist/edge/docker-install/apply.js +458 -0
- package/dist/edge/docker-install/engine.js +362 -0
- package/dist/edge/docker-install/run.js +154 -0
- package/dist/edge/docker-install/state.js +146 -0
- package/dist/edge/{docker-projects → docker-install}/volume.js +1 -5
- package/dist/edge/docker.js +14 -22
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +32 -10
- package/dist/edge/http/app.js +58 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +1 -1
- package/dist/edge/http/folder-location.js +1 -1
- package/dist/edge/http/home.js +39 -0
- package/dist/edge/http/host-cli.js +29 -5
- package/dist/edge/http/narration-peaks.js +76 -0
- package/dist/edge/http/onboarding.js +152 -0
- package/dist/edge/http/open-folder.js +1 -1
- package/dist/edge/http/patch-notes.js +45 -0
- package/dist/edge/http/planning.js +4 -2
- package/dist/edge/http/project-create.js +87 -0
- package/dist/edge/http/projects.js +31 -99
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +86 -0
- package/dist/edge/http/reviews.js +121 -0
- package/dist/edge/http/revision-files.js +1 -1
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +111 -6
- package/dist/edge/http/settings.js +84 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +29 -2
- package/dist/edge/http/studio.js +207 -0
- package/dist/edge/http/style-preview.js +100 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/update.js +24 -3
- package/dist/edge/http/whats-new.js +15 -0
- package/dist/edge/http/youtube-edits.js +83 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +9 -1
- package/dist/kernel/db/migrations/0025-automatic-reviews.sql +40 -0
- package/dist/kernel/db/migrations/0026-schedule-topic-generation.sql +23 -0
- package/dist/kernel/db/migrations/0027-run-cost.sql +58 -0
- package/dist/kernel/db/migrations/0030-automatic-retries.sql +18 -0
- package/dist/kernel/db/migrations/0031-prompt-history-and-description-edits.sql +33 -0
- package/dist/kernel/db/migrations/0032-channels.sql +65 -0
- package/dist/kernel/db/migrations/0034-script-prompts.sql +23 -0
- package/dist/kernel/db/migrations/0035-narration-aliases.sql +13 -0
- package/dist/kernel/db/migrations/0037-project-uploads.sql +8 -0
- package/dist/kernel/db/migrations/0038-voice-languages.sql +7 -0
- package/dist/kernel/db/migrations/0039-channel-essentials.sql +64 -0
- package/dist/kernel/db/migrations/0040-standalone-usage.sql +35 -0
- package/dist/kernel/paths.js +27 -6
- package/dist/kernel/pipeline.js +4 -1
- package/dist/kernel/ports/host-cli.js +122 -14
- package/dist/kernel/ports/key-probe.js +1 -0
- package/dist/kernel/ports/languages.js +157 -0
- package/dist/kernel/ports/model.js +5 -2
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/runner/attempt.js +17 -8
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +24 -5
- package/dist/kernel/runner/index.js +35 -5
- package/dist/kernel/runner/meter.js +1 -0
- package/dist/kernel/runner/providers.js +142 -7
- package/dist/kernel/runner/retry-policy.js +43 -0
- package/dist/kernel/runner/standalone.js +134 -0
- package/dist/kernel/runner/work-authority.js +28 -2
- package/dist/main.js +272 -16
- package/dist/patch-notes/0.4.2-to-1.0.md +93 -0
- package/dist/patch-notes/1.0-to-2.0.md +74 -0
- package/dist/patch-notes/2.0-to-3.0.md +257 -0
- package/dist/patch-notes/3.0.0.md +70 -0
- package/dist/patch-notes/index.json +26 -0
- package/dist/slices/admission/model.js +27 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +79 -1
- package/dist/slices/admission/schema.js +58 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +41 -5
- package/dist/slices/article/continuation.js +2 -2
- package/dist/slices/article/provided-entries.js +2 -2
- package/dist/slices/article/run.js +6 -2
- package/dist/slices/article/segments.js +3 -3
- package/dist/slices/backups/folder.js +14 -6
- package/dist/slices/batch/index.js +7 -2
- package/dist/slices/cancel/index.js +1 -1
- package/dist/slices/channels/cast-images.js +110 -0
- package/dist/slices/channels/cast-match.js +32 -0
- package/dist/slices/channels/images.js +19 -0
- package/dist/slices/channels/model.js +5 -0
- package/dist/slices/channels/rebrand.js +100 -0
- package/dist/slices/channels/repo.js +218 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +129 -0
- package/dist/slices/channels/service.js +180 -0
- package/dist/slices/channels/studio-csv.js +110 -0
- package/dist/slices/channels/videos.js +120 -0
- package/dist/slices/checkpoints/recovery.js +10 -0
- package/dist/slices/control/index.js +2 -1
- package/dist/slices/document/fonts.js +1 -1
- package/dist/slices/document/render.js +1 -1
- package/dist/slices/episodes/related.js +90 -0
- package/dist/slices/episodes/repo.js +60 -0
- package/dist/slices/episodes/service.js +58 -0
- package/dist/slices/episodes/summarize.js +170 -0
- package/dist/slices/estimate/index.js +109 -28
- package/dist/slices/estimate/requests.js +60 -2
- package/dist/slices/fixes/rules.js +73 -0
- package/dist/slices/fonts/coverage.js +82 -0
- package/dist/slices/fonts/sfnt.js +74 -0
- package/dist/slices/images/run.js +8 -5
- package/dist/slices/images/scale.js +84 -0
- package/dist/slices/library/diff.js +122 -0
- package/dist/slices/library/history.js +100 -0
- package/dist/slices/library/model.js +3 -0
- package/dist/slices/library/photorealistic.js +49 -0
- package/dist/slices/library/repo.js +19 -14
- package/dist/slices/library/save.js +62 -12
- package/dist/slices/library/slots.js +7 -1
- package/dist/slices/library/used-by.js +153 -0
- package/dist/slices/loudness/level-pieces.js +41 -0
- package/dist/slices/loudness/loudnorm.js +164 -0
- package/dist/slices/loudness/model.js +153 -0
- package/dist/slices/model-upkeep/model.js +10 -0
- package/dist/slices/model-upkeep/switch.js +217 -0
- package/dist/slices/model-upkeep/usage.js +183 -0
- package/dist/slices/narration/aliases-library.js +31 -0
- package/dist/slices/narration/aliases-schema.js +53 -0
- package/dist/slices/narration/aliases.js +49 -0
- package/dist/slices/narration/blocks.js +352 -0
- package/dist/slices/narration/chunk.js +19 -7
- package/dist/slices/narration/concat.js +2 -2
- package/dist/slices/narration/describe.js +45 -0
- package/dist/slices/narration/pauses-model.js +63 -0
- package/dist/slices/narration/pauses.js +269 -0
- package/dist/slices/narration/peaks-model.js +1 -0
- package/dist/slices/narration/peaks.js +94 -0
- package/dist/slices/narration/preparation.js +21 -3
- package/dist/slices/narration/pronunciation-chunks.js +3 -3
- package/dist/slices/narration/pronunciation.js +72 -25
- package/dist/slices/narration/run.js +6 -6
- package/dist/slices/narration/shared-glossary.js +1 -1
- package/dist/slices/narration/steering.js +15 -4
- package/dist/slices/notifications/notifier.js +21 -3
- package/dist/slices/notifications/rules.js +22 -1
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +46 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +64 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +129 -0
- package/dist/slices/onboarding/sample.js +132 -0
- package/dist/slices/onboarding/state.js +68 -0
- package/dist/slices/onboarding/template.js +73 -0
- package/dist/slices/patch-notes/library.js +73 -0
- package/dist/slices/patch-notes/seen.js +77 -0
- package/dist/slices/play-drafts/convert.js +125 -6
- package/dist/slices/play-drafts/readiness.js +3 -0
- package/dist/slices/play-drafts/repo.js +2 -0
- package/dist/slices/play-drafts/review-inputs.js +20 -3
- package/dist/slices/play-drafts/schema.js +89 -0
- package/dist/slices/play-drafts/service.js +4 -1
- package/dist/slices/play-drafts/start.js +43 -18
- package/dist/slices/project-templates/from-project.js +50 -4
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/service.js +15 -4
- package/dist/slices/project-templates/setup.js +18 -2
- package/dist/slices/rebuild/admission-repo.js +1 -1
- package/dist/slices/rebuild/model.js +6 -0
- package/dist/slices/rebuild/preview-details.js +46 -3
- package/dist/slices/rebuild/preview-plan.js +13 -6
- package/dist/slices/rebuild/recipe-audio-parts.js +40 -7
- package/dist/slices/rebuild/recipe-audio.js +66 -12
- package/dist/slices/rebuild/recipe-build.js +8 -4
- package/dist/slices/rebuild/recipe-cast.js +18 -0
- package/dist/slices/rebuild/recipe-checkpoints.js +11 -9
- package/dist/slices/rebuild/recipe-describe.js +93 -0
- package/dist/slices/rebuild/recipe-edit.js +13 -1
- package/dist/slices/rebuild/recipe-exports.js +62 -8
- package/dist/slices/rebuild/recipe-input-schema.js +35 -0
- package/dist/slices/rebuild/recipe-legacy.js +9 -0
- package/dist/slices/rebuild/recipe-loudness.js +45 -0
- package/dist/slices/rebuild/recipe-model.js +25 -6
- package/dist/slices/rebuild/recipe-pauses.js +7 -0
- package/dist/slices/rebuild/recipe-preparation.js +3 -3
- package/dist/slices/rebuild/recipe-provider-choice.js +11 -1
- package/dist/slices/rebuild/recipe-reference.js +15 -1
- package/dist/slices/rebuild/recipe-reviews.js +117 -0
- package/dist/slices/rebuild/recipe-save.js +4 -1
- package/dist/slices/rebuild/recipe-shorts.js +23 -3
- package/dist/slices/rebuild/recipe-text.js +114 -19
- package/dist/slices/rebuild/recipe-validation.js +14 -2
- package/dist/slices/rebuild/recipe-visual.js +96 -33
- package/dist/slices/rebuild/recipe-voices.js +286 -0
- package/dist/slices/rebuild/recipe-work.js +77 -6
- package/dist/slices/rebuild/recipe-youtube.js +2 -0
- package/dist/slices/rebuild/recovery-conflict.js +7 -3
- package/dist/slices/rebuild/recovery-model.js +3 -0
- package/dist/slices/rebuild/recovery-selection.js +28 -2
- package/dist/slices/rebuild/recovery.js +35 -17
- package/dist/slices/rebuild/repo.js +3 -3
- package/dist/slices/rebuild/retry.js +18 -0
- package/dist/slices/rebuild/review-redo.js +110 -0
- package/dist/slices/rebuild/runtime-actions.js +5 -0
- package/dist/slices/rebuild/runtime-admission.js +4 -1
- package/dist/slices/rebuild/runtime-animate.js +4 -4
- package/dist/slices/rebuild/runtime-article.js +5 -5
- package/dist/slices/rebuild/runtime-document.js +4 -4
- package/dist/slices/rebuild/runtime-export-bed.js +43 -0
- package/dist/slices/rebuild/runtime-export-edit.js +70 -11
- package/dist/slices/rebuild/runtime-export-inputs.js +52 -10
- package/dist/slices/rebuild/runtime-export-short.js +85 -0
- package/dist/slices/rebuild/runtime-export.js +84 -19
- package/dist/slices/rebuild/runtime-figure-card.js +87 -0
- package/dist/slices/rebuild/runtime-image.js +21 -2
- package/dist/slices/rebuild/runtime-local.js +148 -24
- package/dist/slices/rebuild/runtime-materialize.js +8 -0
- package/dist/slices/rebuild/runtime-narration-reuse.js +2 -1
- package/dist/slices/rebuild/runtime-narration-text.js +7 -7
- package/dist/slices/rebuild/runtime-pictures.js +28 -0
- package/dist/slices/rebuild/runtime-piece-label.js +27 -0
- package/dist/slices/rebuild/runtime-provider.js +126 -18
- package/dist/slices/rebuild/runtime-publication.js +21 -5
- package/dist/slices/rebuild/runtime-review.js +237 -0
- package/dist/slices/rebuild/runtime-run.js +17 -11
- package/dist/slices/rebuild/runtime-shorts.js +44 -17
- package/dist/slices/rebuild/runtime-store.js +39 -10
- package/dist/slices/rebuild/runtime-subtitles.js +72 -19
- package/dist/slices/rebuild/runtime-voices.js +103 -0
- package/dist/slices/rebuild/runtime-youtube.js +7 -6
- package/dist/slices/rebuild/soften.js +49 -0
- package/dist/slices/rebuild/transition-repo.js +9 -2
- package/dist/slices/research/run.js +4 -4
- package/dist/slices/research/synthesis.js +2 -2
- package/dist/slices/reviews/model.js +105 -0
- package/dist/slices/reviews/outcome.js +22 -0
- package/dist/slices/reviews/repo.js +106 -0
- package/dist/slices/reviews/rules.js +63 -0
- package/dist/slices/reviews/verdict.js +71 -0
- package/dist/slices/reviews/view.js +30 -0
- package/dist/slices/revisions/adopt-content.js +5 -2
- package/dist/slices/revisions/adopt.js +3 -1
- package/dist/slices/revisions/downloads.js +7 -1
- package/dist/slices/revisions/image-plan.js +183 -0
- package/dist/slices/revisions/mutation-assets.js +10 -0
- package/dist/slices/revisions/mutation-cues.js +71 -0
- package/dist/slices/revisions/mutation-request.js +2 -1
- package/dist/slices/revisions/mutations.js +97 -3
- package/dist/slices/revisions/schema.js +11 -1
- package/dist/slices/run-cost/limits.js +128 -0
- package/dist/slices/run-cost/meter.js +47 -0
- package/dist/slices/run-cost/panel.js +234 -0
- package/dist/slices/run-cost/pricing.js +92 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +158 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +67 -16
- package/dist/slices/schedules/schema.js +138 -1
- package/dist/slices/schedules/service.js +43 -4
- package/dist/slices/schedules/similar.js +90 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +422 -0
- package/dist/slices/settings/first-run.js +102 -0
- package/dist/slices/settings/health.js +190 -0
- package/dist/slices/settings/key-guides.js +128 -0
- package/dist/slices/settings/key-test.js +85 -0
- package/dist/slices/settings/model.js +6 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/voices.js +64 -4
- package/dist/slices/settings/whats-new.js +64 -0
- package/dist/slices/shorts/captions.js +9 -0
- package/dist/slices/shorts/pick.js +3 -3
- package/dist/slices/shorts/prompts.js +1 -1
- package/dist/slices/shorts/render.js +54 -5
- package/dist/slices/storage/asset-name.js +3 -0
- package/dist/slices/storage/backup-export.js +48 -9
- package/dist/slices/storage/backup-format.js +38 -1
- package/dist/slices/storage/backup-import.js +191 -19
- package/dist/slices/storage/delete-project.js +7 -4
- package/dist/slices/storage/documents.js +79 -0
- package/dist/slices/storage/files-location.js +531 -0
- package/dist/slices/storage/layout.js +14 -4
- package/dist/slices/storage/model.js +11 -0
- package/dist/slices/storage/portable.js +92 -12
- package/dist/slices/storage/reconcile.js +14 -1
- package/dist/slices/storage/repo.js +4 -1
- package/dist/slices/storage/schema.js +11 -0
- package/dist/slices/storage/trim.js +146 -0
- package/dist/slices/studio/disclosure.js +74 -0
- package/dist/slices/studio/model.js +28 -0
- package/dist/slices/studio/pack.js +209 -0
- package/dist/slices/studio/settings.js +133 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/render.js +161 -0
- package/dist/slices/style-preview/schema.js +45 -0
- package/dist/slices/style-preview/service.js +93 -0
- package/dist/slices/style-preview/settings.js +89 -0
- package/dist/slices/subtitles/captions.js +42 -9
- package/dist/slices/subtitles/model.js +22 -0
- package/dist/slices/subtitles/prepare.js +27 -16
- package/dist/slices/subtitles/transcript.js +1 -1
- package/dist/slices/thumbnail/run.js +7 -7
- package/dist/slices/trash/model.js +25 -0
- package/dist/slices/trash/service.js +273 -0
- package/dist/slices/uploads/repo.js +20 -0
- package/dist/slices/video/ambient-bed-schema.js +15 -0
- package/dist/slices/video/ambient-bed.js +95 -0
- package/dist/slices/video/ambient-mix.js +72 -0
- package/dist/slices/video/audio-inputs.js +2 -2
- package/dist/slices/video/cards.js +5 -2
- package/dist/slices/video/edit-list.js +23 -2
- package/dist/slices/video/edit-settings.js +13 -3
- package/dist/slices/video/ffmpeg.js +63 -9
- package/dist/slices/video/figure-card.js +492 -0
- package/dist/slices/video/figure-spans.js +51 -0
- package/dist/slices/video/plan.js +125 -13
- package/dist/slices/video/run.js +14 -12
- package/dist/slices/video/slideshow.js +39 -4
- package/dist/slices/voices/attribution.js +69 -0
- package/dist/slices/voices/audio-files.js +82 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +56 -0
- package/dist/slices/voices/grouping.js +49 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +179 -0
- package/dist/slices/voices/palette.js +26 -0
- package/dist/slices/voices/panel.js +91 -0
- package/dist/slices/voices/portraits.js +55 -0
- package/dist/slices/voices/script.js +136 -0
- package/dist/slices/voices/timing.js +58 -0
- package/dist/slices/youtube/answer.js +1 -1
- package/dist/slices/youtube/chapters.js +126 -0
- package/dist/slices/youtube/edits-repo.js +69 -0
- package/dist/slices/youtube/edits.js +89 -0
- package/dist/slices/youtube/placeholders.js +98 -0
- package/dist/slices/youtube/transcript.js +11 -4
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-CUA2sUxU.css +1 -0
- package/dist/web/assets/index-CX590kE-.js +303 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/{pdf-BLF2i8c2.js → pdf-BG8n_uc5.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +4 -4
- package/dist/edge/docker-launch.js +0 -81
- package/dist/edge/docker-projects/activation.js +0 -35
- package/dist/edge/docker-projects/claims.js +0 -85
- package/dist/edge/docker-projects/committed.js +0 -40
- package/dist/edge/docker-projects/engine.js +0 -461
- package/dist/edge/docker-projects/install.js +0 -254
- package/dist/edge/docker-projects/prune.js +0 -66
- package/dist/edge/docker-projects/recover.js +0 -100
- package/dist/edge/docker-projects/state.js +0 -225
- package/dist/web/assets/index-CktpxlJd.js +0 -171
- package/dist/web/assets/index-DB-vOnGU.css +0 -1
- package/scripts/docker-run.sh +0 -3
- /package/dist/edge/{docker-projects → docker-install}/tree.js +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
2
|
+
import { detectSlots } from "../admission/substitute.js";
|
|
3
|
+
import { nameMax } from "../library/model.js";
|
|
4
|
+
import { insertPrompt, promptById, promptByName } from "../library/repo.js";
|
|
5
|
+
import { templateById, templateSummaries } from "../project-templates/repo.js";
|
|
6
|
+
import { createTemplate } from "../project-templates/service.js";
|
|
7
|
+
import { insertVoice, listVoices } from "../settings/repo.js";
|
|
8
|
+
import { packById } from "./packs.js";
|
|
9
|
+
import { readPackRecords, writePackRecord } from "./state.js";
|
|
10
|
+
import { packTemplate } from "./template.js";
|
|
11
|
+
// Installs a pack into the library. Idempotent: an item the pack installed before, and that
|
|
12
|
+
// still exists, is kept as it is, even if it was edited since. A user's own prompt, voice or
|
|
13
|
+
// template is never overwritten: a prompt with the pack's name and the pack's exact text is
|
|
14
|
+
// used as it is, and any other one with that name makes the pack's copy come in as
|
|
15
|
+
// "<name> (2)". `template: false` installs the prompts and the voice only (what the
|
|
16
|
+
// 60-second short needs).
|
|
17
|
+
export function installPack(deps, packId, options = { template: true }) {
|
|
18
|
+
const pack = packById(packId);
|
|
19
|
+
if (pack === undefined)
|
|
20
|
+
return { ok: false, reason: "not-found" };
|
|
21
|
+
return transact(deps.db, () => {
|
|
22
|
+
const before = readPackRecords(deps.db)[pack.id];
|
|
23
|
+
let added = false;
|
|
24
|
+
const at = deps.clock.now().toISOString();
|
|
25
|
+
const prompts = {};
|
|
26
|
+
const names = {};
|
|
27
|
+
for (const prompt of pack.prompts) {
|
|
28
|
+
const kept = before?.prompts[prompt.key];
|
|
29
|
+
const existing = kept === undefined ? undefined : promptById(deps.db, kept);
|
|
30
|
+
if (existing !== undefined) {
|
|
31
|
+
prompts[prompt.key] = existing.id;
|
|
32
|
+
names[prompt.key] = existing.name;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const placed = placePrompt(deps, prompt.kind, prompt.name, prompt.body, at);
|
|
36
|
+
added ||= placed.added;
|
|
37
|
+
prompts[prompt.key] = placed.prompt.id;
|
|
38
|
+
names[prompt.key] = placed.prompt.name;
|
|
39
|
+
}
|
|
40
|
+
const voice = placeVoice(deps, pack, before);
|
|
41
|
+
added ||= voice.added;
|
|
42
|
+
let template = before?.template;
|
|
43
|
+
if (options.template && (template === undefined || !templateById(deps.db, template))) {
|
|
44
|
+
const id = deps.uuid();
|
|
45
|
+
const created = createTemplate(deps, {
|
|
46
|
+
id,
|
|
47
|
+
name: freeTemplateName(deps, `${pack.name} starter`),
|
|
48
|
+
document: packTemplate(pack, names, voice.voiceId),
|
|
49
|
+
});
|
|
50
|
+
if (!created.ok)
|
|
51
|
+
throw new Error(`Slopify hit an internal error (the ${pack.name} pack's template was refused: ${created.reason}). Try again; if it happens again, use Download diagnostics in Settings and report it.`);
|
|
52
|
+
template = id;
|
|
53
|
+
added = true;
|
|
54
|
+
}
|
|
55
|
+
const record = {
|
|
56
|
+
installedAt: before?.installedAt ?? at,
|
|
57
|
+
prompts,
|
|
58
|
+
voice: voice.id,
|
|
59
|
+
...(template === undefined ? {} : { template }),
|
|
60
|
+
};
|
|
61
|
+
writePackRecord(deps.db, pack.id, record);
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
value: {
|
|
65
|
+
packId: pack.id,
|
|
66
|
+
added,
|
|
67
|
+
prompts: names,
|
|
68
|
+
templateId: template ?? null,
|
|
69
|
+
voice: voice.voiceId,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function placePrompt(deps, kind, name, body, at) {
|
|
75
|
+
for (let n = 1;; n += 1) {
|
|
76
|
+
const suffix = n === 1 ? "" : ` (${String(n)})`;
|
|
77
|
+
const candidate = `${name.slice(0, nameMax - suffix.length).trimEnd()}${suffix}`;
|
|
78
|
+
const taken = promptByName(deps.db, kind, candidate);
|
|
79
|
+
if (taken?.body === body)
|
|
80
|
+
return { prompt: taken, added: false };
|
|
81
|
+
if (taken !== undefined)
|
|
82
|
+
continue;
|
|
83
|
+
const prompt = {
|
|
84
|
+
id: deps.ids.next(),
|
|
85
|
+
kind,
|
|
86
|
+
name: candidate,
|
|
87
|
+
body,
|
|
88
|
+
slots: detectSlots(body).names,
|
|
89
|
+
updatedAt: at,
|
|
90
|
+
};
|
|
91
|
+
insertPrompt(deps.db, prompt);
|
|
92
|
+
return { prompt, added: true };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// The suggested voice as a saved voice, so Play's voice list offers it. One the user already
|
|
96
|
+
// saved under the same voice ID is used as it is, name and all.
|
|
97
|
+
function placeVoice(deps, pack, before) {
|
|
98
|
+
const voices = listVoices(deps.db);
|
|
99
|
+
const same = voices.find((voice) => (before?.voice !== undefined && voice.id === before.voice) ||
|
|
100
|
+
(voice.provider === pack.voice.provider && voice.voiceId === pack.voice.voiceId));
|
|
101
|
+
if (same !== undefined)
|
|
102
|
+
return { id: same.id, voiceId: same.voiceId, added: false };
|
|
103
|
+
const id = deps.ids.next();
|
|
104
|
+
insertVoice(deps.db, {
|
|
105
|
+
id,
|
|
106
|
+
provider: pack.voice.provider,
|
|
107
|
+
name: pack.voice.name,
|
|
108
|
+
voiceId: pack.voice.voiceId,
|
|
109
|
+
});
|
|
110
|
+
return { id, voiceId: pack.voice.voiceId, added: true };
|
|
111
|
+
}
|
|
112
|
+
function freeTemplateName(deps, name) {
|
|
113
|
+
const taken = new Set(templateSummaries(deps.db).map((row) => row.name.toLowerCase()));
|
|
114
|
+
for (let n = 1;; n += 1) {
|
|
115
|
+
const candidate = n === 1 ? name : `${name} (${String(n)})`;
|
|
116
|
+
if (!taken.has(candidate.toLowerCase()))
|
|
117
|
+
return candidate;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
// The first five minutes' wire shapes. Browser-safe: the first-run screen, the Library's
|
|
3
|
+
// packs and the sample's page read these too.
|
|
4
|
+
// The bundled samples: "The Library of Alexandria" (narrated video, shorts, article, PDF) and
|
|
5
|
+
// two multi-voice demos, an audiobook and a podcast.
|
|
6
|
+
export const sampleIds = ["library", "audiobook", "podcast"];
|
|
7
|
+
// Each sample's project, or null while it is not in Projects.
|
|
8
|
+
export const sampleProjectsSchema = z.object({
|
|
9
|
+
library: z.string().nullable(),
|
|
10
|
+
audiobook: z.string().nullable(),
|
|
11
|
+
podcast: z.string().nullable(),
|
|
12
|
+
});
|
|
13
|
+
export const firstRunViewSchema = z
|
|
14
|
+
.object({
|
|
15
|
+
show: z.boolean(),
|
|
16
|
+
// The Library of Alexandria's project, as before the demos; `samples` has all three.
|
|
17
|
+
sampleProjectId: z.string().nullable(),
|
|
18
|
+
samples: sampleProjectsSchema,
|
|
19
|
+
clis: z.array(z.object({
|
|
20
|
+
id: z.enum(["claude-code", "codex", "gemini"]),
|
|
21
|
+
name: z.string(),
|
|
22
|
+
installed: z.boolean(),
|
|
23
|
+
ready: z.boolean(),
|
|
24
|
+
version: z.string().nullable(),
|
|
25
|
+
issue: z.string().nullable(),
|
|
26
|
+
// Codex draws the images too.
|
|
27
|
+
draws: z.boolean(),
|
|
28
|
+
})),
|
|
29
|
+
packs: z.array(z.object({
|
|
30
|
+
id: z.string(),
|
|
31
|
+
name: z.string(),
|
|
32
|
+
summary: z.string(),
|
|
33
|
+
installed: z.boolean(),
|
|
34
|
+
templateId: z.string().nullable(),
|
|
35
|
+
})),
|
|
36
|
+
})
|
|
37
|
+
.readonly();
|
|
38
|
+
export const quickShortInputSchema = z
|
|
39
|
+
.object({
|
|
40
|
+
topic: z.string().trim().min(1).max(200),
|
|
41
|
+
packId: z.string().max(40).optional(),
|
|
42
|
+
requestId: z.uuid(),
|
|
43
|
+
})
|
|
44
|
+
.strict();
|
|
45
|
+
export const quickShortResultSchema = z.object({
|
|
46
|
+
projectId: z.string(),
|
|
47
|
+
replayed: z.boolean(),
|
|
48
|
+
});
|
|
49
|
+
export const installedPackSchema = z.object({
|
|
50
|
+
packId: z.string(),
|
|
51
|
+
added: z.boolean(),
|
|
52
|
+
prompts: z.record(z.string(), z.string()),
|
|
53
|
+
templateId: z.string().nullable(),
|
|
54
|
+
voice: z.string().nullable(),
|
|
55
|
+
});
|
|
56
|
+
export const sampleCopySchema = z.object({ projectId: z.string() });
|
|
57
|
+
export const sampleStateSchema = z.object({
|
|
58
|
+
projectId: z.string().nullable(),
|
|
59
|
+
samples: sampleProjectsSchema,
|
|
60
|
+
});
|
|
61
|
+
// Make my own copy of one sample; without a project, the Library of Alexandria.
|
|
62
|
+
export const sampleCopyInputSchema = z
|
|
63
|
+
.object({ projectId: z.string().max(60).optional() })
|
|
64
|
+
.strict();
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
import { defaultVideoEdit } from "../video/edit-settings.js";
|
|
2
|
+
const look = (grade, change = {}) => ({
|
|
3
|
+
...defaultVideoEdit,
|
|
4
|
+
grade,
|
|
5
|
+
...change,
|
|
6
|
+
});
|
|
7
|
+
const noText = "No text, letters, captions, logos or watermarks anywhere in the image.";
|
|
8
|
+
const shortsRules = (voice) => [
|
|
9
|
+
"Pick the moments of this video that work best as vertical shorts.",
|
|
10
|
+
"",
|
|
11
|
+
"- Each short must stand on its own for someone who never saw the video.",
|
|
12
|
+
`- ${voice}`,
|
|
13
|
+
"- Start on the strongest line of the moment, never on a transition or a recap.",
|
|
14
|
+
"- End on a finished thought.",
|
|
15
|
+
"- Title: at most 60 characters, plain words, no clickbait, no hashtags.",
|
|
16
|
+
"- Description: one line saying what the viewer gets from the clip.",
|
|
17
|
+
"- Hashtags: 3-5, about the subject of that clip, most specific last.",
|
|
18
|
+
].join("\n");
|
|
19
|
+
const scriptRules = [
|
|
20
|
+
"Write the narration for a vertical short video of about 60 seconds: 140-160 words.",
|
|
21
|
+
"",
|
|
22
|
+
"- Plain prose only: no headings, lists, stage directions, emoji or hashtags. Every word is spoken.",
|
|
23
|
+
"- The first sentence is the hook. It must make someone stop scrolling on its own.",
|
|
24
|
+
"- One idea, told as a small story with a turn near the end. No introduction, no recap, no 'in this video'.",
|
|
25
|
+
"- Short sentences that read well aloud. Spell out numbers the way they are said.",
|
|
26
|
+
"- End on a line that lands, not on a call to subscribe.",
|
|
27
|
+
"- Stay accurate. If something is uncertain or disputed, say so in a few words instead of inventing detail.",
|
|
28
|
+
].join("\n");
|
|
29
|
+
export const starterPacks = [
|
|
30
|
+
{
|
|
31
|
+
id: "sleep-lore",
|
|
32
|
+
name: "Sleep lore",
|
|
33
|
+
summary: "Slow, gentle myths and legends told to fall asleep to.",
|
|
34
|
+
voice: {
|
|
35
|
+
provider: "openai-tts",
|
|
36
|
+
model: "gpt-4o-mini-tts",
|
|
37
|
+
voiceId: "sage",
|
|
38
|
+
name: "Sleep lore narrator (calm)",
|
|
39
|
+
},
|
|
40
|
+
style: {
|
|
41
|
+
research: false,
|
|
42
|
+
expectedWords: 3000,
|
|
43
|
+
imagesPerVideo: 16,
|
|
44
|
+
imageSeconds: 24,
|
|
45
|
+
zoomPercent: 8,
|
|
46
|
+
motionStyle: "pan",
|
|
47
|
+
captions: { fontSize: 44, position: "bottom" },
|
|
48
|
+
videoEdit: look("warm", {
|
|
49
|
+
transition: "crossfade",
|
|
50
|
+
transitionSeconds: 2,
|
|
51
|
+
vignette: "subtle",
|
|
52
|
+
atmosphere: "fog",
|
|
53
|
+
}),
|
|
54
|
+
},
|
|
55
|
+
prompts: [
|
|
56
|
+
{
|
|
57
|
+
key: "article",
|
|
58
|
+
kind: "article",
|
|
59
|
+
name: "Sleep lore · Story",
|
|
60
|
+
body: [
|
|
61
|
+
"Write a long, calm bedtime telling of {{topic}} for listeners who want to drift off to sleep.",
|
|
62
|
+
"",
|
|
63
|
+
"Length: 2800-3200 words.",
|
|
64
|
+
"",
|
|
65
|
+
"Voice and pace:",
|
|
66
|
+
"- Warm, unhurried and low in energy, like someone reading by lamplight. Nothing sudden, loud or frightening; soften any violence in the source into distance and suggestion.",
|
|
67
|
+
"- Long, flowing sentences with gentle rhythm. Linger on places, weather, light, textures and small sounds.",
|
|
68
|
+
"- Speak to the listener now and then ('you might picture...'), never more than once a section.",
|
|
69
|
+
"",
|
|
70
|
+
"Shape:",
|
|
71
|
+
"- Open by setting a quiet scene, not with the plot.",
|
|
72
|
+
"- Follow the story in order, in six to eight parts, each with a short heading.",
|
|
73
|
+
"- No cliffhangers and no questions left hanging. Each part settles before the next begins.",
|
|
74
|
+
"- Close with a slow, peaceful ending that asks nothing of the listener.",
|
|
75
|
+
"",
|
|
76
|
+
"Accuracy: tell the story as the traditional sources tell it. Where versions differ, choose one and mention the other in a single soft sentence. Do not invent names, dates or quotations.",
|
|
77
|
+
"",
|
|
78
|
+
"Return only the story in Markdown: the headings and the prose. No title page, notes or sources.",
|
|
79
|
+
].join("\n"),
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
key: "shortScript",
|
|
83
|
+
kind: "article",
|
|
84
|
+
name: "Sleep lore · 60-second short",
|
|
85
|
+
body: [
|
|
86
|
+
`Subject: {{topic}}, as a quiet moment from myth or legend.`,
|
|
87
|
+
"",
|
|
88
|
+
scriptRules,
|
|
89
|
+
"- Keep the calm, hushed tone of a bedtime story even in the hook.",
|
|
90
|
+
].join("\n"),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: "image",
|
|
94
|
+
kind: "image",
|
|
95
|
+
name: "Sleep lore · Scene",
|
|
96
|
+
body: [
|
|
97
|
+
"A dreamlike painted scene from the legend of {{topic}}.",
|
|
98
|
+
"Soft oil-painting style with visible brushwork, muted twilight palette of deep blues, dusky violets and warm lantern gold.",
|
|
99
|
+
"Gentle haze, low contrast, wide calm composition with plenty of quiet space; distant figures rather than close faces.",
|
|
100
|
+
"Peaceful and still, nothing threatening in the frame.",
|
|
101
|
+
noText,
|
|
102
|
+
].join(" "),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
key: "thumbnail",
|
|
106
|
+
kind: "thumbnail",
|
|
107
|
+
name: "Sleep lore · Thumbnail",
|
|
108
|
+
body: [
|
|
109
|
+
"A serene, luminous painting that sums up the legend of {{topic}} in one image.",
|
|
110
|
+
"One clear subject in the centre, soft moonlight or candlelight, deep blue and gold palette, gentle mist.",
|
|
111
|
+
"Readable at a small size: simple shapes, strong silhouette, calm mood.",
|
|
112
|
+
noText,
|
|
113
|
+
].join(" "),
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
key: "description",
|
|
117
|
+
kind: "description",
|
|
118
|
+
name: "Sleep lore · Description",
|
|
119
|
+
body: [
|
|
120
|
+
"Write a YouTube description for a sleep story about {{topic}}.",
|
|
121
|
+
"",
|
|
122
|
+
"- Summary: 2-3 soothing sentences that invite the viewer to settle in. Mention that the story is calm and has no sudden sounds.",
|
|
123
|
+
"- Chapters: one per part of the story, 3-6 gentle words each.",
|
|
124
|
+
"- Hashtags: 3-5, such as sleep story and the legend's subject, most specific last.",
|
|
125
|
+
"- Tags: 15-25 search terms for sleep stories, bedtime myths and the legend itself.",
|
|
126
|
+
].join("\n"),
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
key: "shorts",
|
|
130
|
+
kind: "shorts",
|
|
131
|
+
name: "Sleep lore · Shorts",
|
|
132
|
+
body: shortsRules("Prefer the most beautiful or mysterious images of the story over plot summaries."),
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: "true-crime",
|
|
138
|
+
name: "True crime",
|
|
139
|
+
summary: "Measured, factual retellings of real cases and their investigations.",
|
|
140
|
+
voice: {
|
|
141
|
+
provider: "openai-tts",
|
|
142
|
+
model: "gpt-4o-mini-tts",
|
|
143
|
+
voiceId: "onyx",
|
|
144
|
+
name: "True crime narrator (deep)",
|
|
145
|
+
},
|
|
146
|
+
style: {
|
|
147
|
+
research: true,
|
|
148
|
+
expectedWords: 2400,
|
|
149
|
+
imagesPerVideo: 18,
|
|
150
|
+
imageSeconds: 10,
|
|
151
|
+
zoomPercent: 15,
|
|
152
|
+
motionStyle: "zoom",
|
|
153
|
+
captions: { fontSize: 52, position: "bottom" },
|
|
154
|
+
videoEdit: look("desaturated", {
|
|
155
|
+
transition: "fadeblack",
|
|
156
|
+
transitionSeconds: 0.8,
|
|
157
|
+
vignette: "strong",
|
|
158
|
+
grain: "subtle",
|
|
159
|
+
}),
|
|
160
|
+
},
|
|
161
|
+
prompts: [
|
|
162
|
+
{
|
|
163
|
+
key: "article",
|
|
164
|
+
kind: "article",
|
|
165
|
+
name: "True crime · Case",
|
|
166
|
+
body: [
|
|
167
|
+
"Write a documentary narration about the case of {{topic}}.",
|
|
168
|
+
"",
|
|
169
|
+
"Length: 2200-2600 words.",
|
|
170
|
+
"",
|
|
171
|
+
"Tone: measured, serious and respectful of the victims and their families. Build tension from facts and timing, never from gore or speculation. No graphic detail of injuries.",
|
|
172
|
+
"",
|
|
173
|
+
"Shape:",
|
|
174
|
+
"- Open with one concrete moment that sets the stakes, then step back to who the people were.",
|
|
175
|
+
"- Tell the events in order, then the investigation: what was found, when, and what it meant.",
|
|
176
|
+
"- Give the outcome as it stands today: charges, trial, verdict, appeals, or that it is unsolved.",
|
|
177
|
+
"- Close by returning to the people at the centre of the case, not to the offender.",
|
|
178
|
+
"- Six to eight sections with short, plain headings.",
|
|
179
|
+
"",
|
|
180
|
+
"Accuracy rules:",
|
|
181
|
+
"- Use only facts supported by the research notes or reliable public records. If a detail is disputed or unproven, say so plainly ('police believed', 'the defence argued').",
|
|
182
|
+
"- Name a living person who was never convicted only as the sources do, and never imply guilt.",
|
|
183
|
+
"- Do not invent dialogue, quotes, times or places.",
|
|
184
|
+
"",
|
|
185
|
+
"Return only the narration in Markdown: headings and prose. No title page and no source list.",
|
|
186
|
+
].join("\n"),
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
key: "shortScript",
|
|
190
|
+
kind: "article",
|
|
191
|
+
name: "True crime · 60-second short",
|
|
192
|
+
body: [
|
|
193
|
+
"Subject: one striking, well-documented moment from the case of {{topic}}.",
|
|
194
|
+
"",
|
|
195
|
+
scriptRules,
|
|
196
|
+
"- Respectful and factual: no gore, no speculation about guilt, no invented detail.",
|
|
197
|
+
].join("\n"),
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
key: "image",
|
|
201
|
+
kind: "image",
|
|
202
|
+
name: "True crime · Scene",
|
|
203
|
+
body: [
|
|
204
|
+
"A moody documentary-style photograph evoking the setting of {{topic}}: the place, the era, the objects, the weather.",
|
|
205
|
+
"Cinematic 35mm look, overcast or night light, desaturated cool palette with deep shadows and a single practical light source.",
|
|
206
|
+
"Empty streets, closed doors, rain on glass, paperwork on a desk; no recognisable real people, no bodies, no blood, no weapons pointed at anyone.",
|
|
207
|
+
noText,
|
|
208
|
+
].join(" "),
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
key: "thumbnail",
|
|
212
|
+
kind: "thumbnail",
|
|
213
|
+
name: "True crime · Thumbnail",
|
|
214
|
+
body: [
|
|
215
|
+
"A tense cinematic image that sums up the case of {{topic}}: one strong object or place in sharp focus against a dark, blurred background.",
|
|
216
|
+
"High contrast, cold blue and amber light, heavy vignette, readable at phone size.",
|
|
217
|
+
"No real people's faces, no gore.",
|
|
218
|
+
noText,
|
|
219
|
+
].join(" "),
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
key: "description",
|
|
223
|
+
kind: "description",
|
|
224
|
+
name: "True crime · Description",
|
|
225
|
+
body: [
|
|
226
|
+
"Write a YouTube description for a documentary about the case of {{topic}}.",
|
|
227
|
+
"",
|
|
228
|
+
"- Summary: 2-3 factual sentences on what happened and what the video covers. No sensational words.",
|
|
229
|
+
"- Chapters: split where the story moves from events to investigation to outcome; 3-6 plain words each.",
|
|
230
|
+
"- Hashtags: 3-5 about the case and true crime, most specific last.",
|
|
231
|
+
"- Tags: 15-25 search terms for the case, the place, the people named in the sources and true crime documentaries.",
|
|
232
|
+
"- End the summary with one line noting that the video discusses real crimes.",
|
|
233
|
+
].join("\n"),
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
key: "shorts",
|
|
237
|
+
kind: "shorts",
|
|
238
|
+
name: "True crime · Shorts",
|
|
239
|
+
body: shortsRules("Prefer turning points of the investigation and verified surprising facts; never pick a moment that dwells on violence."),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: "history",
|
|
245
|
+
name: "History",
|
|
246
|
+
summary: "Narrative history of people, places and events, told like a documentary.",
|
|
247
|
+
voice: {
|
|
248
|
+
provider: "openai-tts",
|
|
249
|
+
model: "gpt-4o-mini-tts",
|
|
250
|
+
voiceId: "fable",
|
|
251
|
+
name: "History narrator (storyteller)",
|
|
252
|
+
},
|
|
253
|
+
style: {
|
|
254
|
+
research: true,
|
|
255
|
+
expectedWords: 2200,
|
|
256
|
+
imagesPerVideo: 16,
|
|
257
|
+
imageSeconds: 12,
|
|
258
|
+
zoomPercent: 20,
|
|
259
|
+
motionStyle: "mixed",
|
|
260
|
+
captions: { fontSize: 48, position: "bottom" },
|
|
261
|
+
videoEdit: look("sepia", {
|
|
262
|
+
transition: "crossfade",
|
|
263
|
+
transitionSeconds: 1,
|
|
264
|
+
vignette: "subtle",
|
|
265
|
+
grain: "subtle",
|
|
266
|
+
atmosphere: "dust",
|
|
267
|
+
chapterCards: true,
|
|
268
|
+
}),
|
|
269
|
+
},
|
|
270
|
+
prompts: [
|
|
271
|
+
{
|
|
272
|
+
key: "article",
|
|
273
|
+
kind: "article",
|
|
274
|
+
name: "History · Documentary",
|
|
275
|
+
body: [
|
|
276
|
+
"Write a documentary narration about {{topic}}.",
|
|
277
|
+
"",
|
|
278
|
+
"Length: 2000-2400 words.",
|
|
279
|
+
"",
|
|
280
|
+
"Tone: vivid and curious, like a good museum guide who loves the subject. Explain terms the first time they appear. Avoid modern slang and avoid judging the past by today's words without saying so.",
|
|
281
|
+
"",
|
|
282
|
+
"Shape:",
|
|
283
|
+
"- Open in a specific scene: a place, a year and a person doing something.",
|
|
284
|
+
"- Then the background a newcomer needs, in a few paragraphs.",
|
|
285
|
+
"- The main story in order, with the causes and consequences made clear.",
|
|
286
|
+
"- A section on how we know: the sources, what archaeology or documents show, and what historians still argue about.",
|
|
287
|
+
"- Close with what changed because of it and what survives today.",
|
|
288
|
+
"- Six to nine sections with short headings.",
|
|
289
|
+
"",
|
|
290
|
+
"Accuracy: stay with what the research notes and mainstream scholarship support. Give dates where they matter. Mark legends as legends and estimates as estimates. Never invent quotations.",
|
|
291
|
+
"",
|
|
292
|
+
"Return only the narration in Markdown: headings and prose. No title page and no source list.",
|
|
293
|
+
].join("\n"),
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
key: "shortScript",
|
|
297
|
+
kind: "article",
|
|
298
|
+
name: "History · 60-second short",
|
|
299
|
+
body: [
|
|
300
|
+
"Subject: one surprising, true story or detail about {{topic}}.",
|
|
301
|
+
"",
|
|
302
|
+
scriptRules,
|
|
303
|
+
"- Put the year and the place in the first two sentences.",
|
|
304
|
+
].join("\n"),
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
key: "image",
|
|
308
|
+
kind: "image",
|
|
309
|
+
name: "History · Scene",
|
|
310
|
+
body: [
|
|
311
|
+
"A historically grounded illustration of {{topic}}: period-accurate clothing, architecture, tools and landscape.",
|
|
312
|
+
"Rich painterly realism in the manner of a museum reconstruction, warm golden light, earthy ochre, umber and faded blue palette.",
|
|
313
|
+
"Wide establishing views and mid shots of daily life; figures shown at a distance or from behind rather than as portraits of real people.",
|
|
314
|
+
noText,
|
|
315
|
+
].join(" "),
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
key: "thumbnail",
|
|
319
|
+
kind: "thumbnail",
|
|
320
|
+
name: "History · Thumbnail",
|
|
321
|
+
body: [
|
|
322
|
+
"A dramatic, detailed painting that sums up {{topic}} in one moment: one clear focal subject, strong light from one side, dust or smoke in the air.",
|
|
323
|
+
"Warm ochre and deep blue palette, high contrast, simple composition that reads at phone size.",
|
|
324
|
+
noText,
|
|
325
|
+
].join(" "),
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
key: "description",
|
|
329
|
+
kind: "description",
|
|
330
|
+
name: "History · Description",
|
|
331
|
+
body: [
|
|
332
|
+
"Write a YouTube description for a history documentary about {{topic}}.",
|
|
333
|
+
"",
|
|
334
|
+
"- Summary: 2-3 sentences that name the period and place and say what the viewer will understand by the end.",
|
|
335
|
+
"- Chapters: one per section of the narration, 2-6 words each, in title case.",
|
|
336
|
+
"- Hashtags: 3-5 about the period and subject, most specific last.",
|
|
337
|
+
"- Tags: 15-25 search terms for the event, the people, the place, the century and history documentaries.",
|
|
338
|
+
].join("\n"),
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
key: "shorts",
|
|
342
|
+
kind: "shorts",
|
|
343
|
+
name: "History · Shorts",
|
|
344
|
+
body: shortsRules("Prefer little-known facts, turning points and vivid scenes over dates and summaries."),
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: "science",
|
|
350
|
+
name: "Science explainers",
|
|
351
|
+
summary: "Clear, friendly explanations of how the world works, one idea at a time.",
|
|
352
|
+
voice: {
|
|
353
|
+
provider: "openai-tts",
|
|
354
|
+
model: "gpt-4o-mini-tts",
|
|
355
|
+
voiceId: "nova",
|
|
356
|
+
name: "Science narrator (bright)",
|
|
357
|
+
},
|
|
358
|
+
style: {
|
|
359
|
+
research: true,
|
|
360
|
+
expectedWords: 1600,
|
|
361
|
+
imagesPerVideo: 14,
|
|
362
|
+
imageSeconds: 9,
|
|
363
|
+
zoomPercent: 15,
|
|
364
|
+
motionStyle: "zoom",
|
|
365
|
+
captions: { fontSize: 50, position: "bottom" },
|
|
366
|
+
videoEdit: look("cold", { transition: "slide", transitionSeconds: 0.6 }),
|
|
367
|
+
},
|
|
368
|
+
prompts: [
|
|
369
|
+
{
|
|
370
|
+
key: "article",
|
|
371
|
+
kind: "article",
|
|
372
|
+
name: "Science · Explainer",
|
|
373
|
+
body: [
|
|
374
|
+
"Write an explainer video narration about {{topic}} for curious viewers with no science background.",
|
|
375
|
+
"",
|
|
376
|
+
"Length: 1400-1800 words.",
|
|
377
|
+
"",
|
|
378
|
+
"Tone: friendly, clear and precise. Enthusiastic without hype. Use 'we' for shared discovery.",
|
|
379
|
+
"",
|
|
380
|
+
"Shape:",
|
|
381
|
+
"- Open with an everyday observation or a puzzling question the topic answers.",
|
|
382
|
+
"- Build the idea step by step: each section adds one concept and ends where the next begins.",
|
|
383
|
+
"- Use one concrete analogy per hard idea, then say where the analogy breaks down.",
|
|
384
|
+
"- Give real numbers with units and a comparison that makes them graspable.",
|
|
385
|
+
"- A short section on what scientists still don't know or are testing now.",
|
|
386
|
+
"- Close by returning to the opening question, answered.",
|
|
387
|
+
"- Five to seven sections with short headings.",
|
|
388
|
+
"",
|
|
389
|
+
"Accuracy: follow the research notes and the current scientific consensus. Separate established results from hypotheses. Do not invent studies, figures or quotations.",
|
|
390
|
+
"",
|
|
391
|
+
"Return only the narration in Markdown: headings and prose. No title page and no source list.",
|
|
392
|
+
].join("\n"),
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
key: "shortScript",
|
|
396
|
+
kind: "article",
|
|
397
|
+
name: "Science · 60-second short",
|
|
398
|
+
body: [
|
|
399
|
+
"Subject: one mind-bending but true fact about {{topic}}, and why it is so.",
|
|
400
|
+
"",
|
|
401
|
+
scriptRules,
|
|
402
|
+
"- Give one real number with its unit and a comparison that makes it graspable.",
|
|
403
|
+
].join("\n"),
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
key: "image",
|
|
407
|
+
kind: "image",
|
|
408
|
+
name: "Science · Visual",
|
|
409
|
+
body: [
|
|
410
|
+
"A clean, striking visual that explains an aspect of {{topic}}.",
|
|
411
|
+
"Modern 3D-rendered or macro-photography look, crisp detail, dark background with vivid cyan, magenta and amber accents, soft rim light.",
|
|
412
|
+
"One clear subject, scientifically plausible shapes and scales, generous negative space.",
|
|
413
|
+
noText,
|
|
414
|
+
].join(" "),
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
key: "thumbnail",
|
|
418
|
+
kind: "thumbnail",
|
|
419
|
+
name: "Science · Thumbnail",
|
|
420
|
+
body: [
|
|
421
|
+
"A bold, eye-catching image about {{topic}}: one surprising subject, glowing against a deep dark background.",
|
|
422
|
+
"Saturated cyan and amber, strong contrast, simple composition that reads at phone size.",
|
|
423
|
+
noText,
|
|
424
|
+
].join(" "),
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
key: "description",
|
|
428
|
+
kind: "description",
|
|
429
|
+
name: "Science · Description",
|
|
430
|
+
body: [
|
|
431
|
+
"Write a YouTube description for a science explainer about {{topic}}.",
|
|
432
|
+
"",
|
|
433
|
+
"- Summary: 2-3 sentences that pose the question and promise a clear answer, without exaggeration.",
|
|
434
|
+
"- Chapters: one per step of the explanation, 2-6 words each.",
|
|
435
|
+
"- Hashtags: 3-5 about the field and the topic, most specific last.",
|
|
436
|
+
"- Tags: 15-25 search terms, from the broad field to the specific phenomenon.",
|
|
437
|
+
].join("\n"),
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
key: "shorts",
|
|
441
|
+
kind: "shorts",
|
|
442
|
+
name: "Science · Shorts",
|
|
443
|
+
body: shortsRules("Prefer the most surprising facts and the clearest 'aha' explanations."),
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
},
|
|
447
|
+
];
|
|
448
|
+
// What "Make a 60-second short" installs when no pack is picked.
|
|
449
|
+
export const starterSet = {
|
|
450
|
+
id: "starter",
|
|
451
|
+
name: "Starter",
|
|
452
|
+
summary: "What a first short uses when no pack is picked.",
|
|
453
|
+
voice: {
|
|
454
|
+
provider: "openai-tts",
|
|
455
|
+
model: "gpt-4o-mini-tts",
|
|
456
|
+
voiceId: "alloy",
|
|
457
|
+
name: "Starter narrator",
|
|
458
|
+
},
|
|
459
|
+
style: {
|
|
460
|
+
research: false,
|
|
461
|
+
expectedWords: 150,
|
|
462
|
+
imagesPerVideo: 4,
|
|
463
|
+
imageSeconds: 15,
|
|
464
|
+
zoomPercent: 15,
|
|
465
|
+
motionStyle: "zoom",
|
|
466
|
+
captions: { fontSize: 48, position: "bottom" },
|
|
467
|
+
videoEdit: defaultVideoEdit,
|
|
468
|
+
},
|
|
469
|
+
prompts: [
|
|
470
|
+
{
|
|
471
|
+
key: "shortScript",
|
|
472
|
+
kind: "article",
|
|
473
|
+
name: "Starter · 60-second short",
|
|
474
|
+
body: ["Subject: {{topic}}.", "", scriptRules].join("\n"),
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
key: "image",
|
|
478
|
+
kind: "image",
|
|
479
|
+
name: "Starter · Vertical scene",
|
|
480
|
+
body: [
|
|
481
|
+
"A cinematic vertical image about {{topic}}.",
|
|
482
|
+
"Rich colour, dramatic light, one clear subject large in the frame so it reads on a phone.",
|
|
483
|
+
"Keep the lower half calm: captions are drawn over it.",
|
|
484
|
+
noText,
|
|
485
|
+
].join(" "),
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
};
|
|
489
|
+
export function packById(id) {
|
|
490
|
+
return id === starterSet.id ? starterSet : starterPacks.find((pack) => pack.id === id);
|
|
491
|
+
}
|