@gentbajko/slopify 2.5.0 → 3.0.1
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 +24 -41
- package/SUBTITLES.md +1 -1
- package/dist/adapter-registry.js +31 -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 +85 -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/gemini.js +206 -0
- 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/pcm-mp3.js +62 -0
- package/dist/adapters/tts/system.js +147 -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/models.yaml +38 -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/assets/style-preview/narration.mp3 +0 -0
- package/dist/assets/style-preview/tall-1.jpg +0 -0
- package/dist/assets/style-preview/tall-2.jpg +0 -0
- package/dist/assets/style-preview/tall-3.jpg +0 -0
- package/dist/assets/style-preview/wide-1.jpg +0 -0
- package/dist/assets/style-preview/wide-2.jpg +0 -0
- package/dist/assets/style-preview/wide-3.jpg +0 -0
- package/dist/catalog/merge.js +132 -0
- package/dist/catalog/registry.js +17 -14
- package/dist/catalog/schema.js +2 -0
- package/dist/catalog/store.js +147 -8
- package/dist/catalog/validate.js +2 -2
- package/dist/compose.yaml +71 -0
- package/dist/edge/autostart/docker-record.js +32 -0
- package/dist/edge/autostart/docker.js +118 -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 +72 -0
- package/dist/edge/autostart/service.js +90 -0
- package/dist/edge/cli-args.js +71 -0
- package/dist/edge/cli.js +64 -63
- 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 +160 -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/events/hub.js +3 -1
- package/dist/edge/host-cli.js +1 -1
- package/dist/edge/http/actions.js +38 -11
- package/dist/edge/http/app.js +60 -2
- package/dist/edge/http/auditions.js +70 -0
- package/dist/edge/http/autostart.js +37 -0
- package/dist/edge/http/byte-range.js +42 -0
- package/dist/edge/http/channel-memory.js +92 -0
- package/dist/edge/http/channels.js +128 -0
- package/dist/edge/http/diagnostics.js +1 -0
- package/dist/edge/http/draft-files.js +2 -4
- package/dist/edge/http/entries.js +22 -3
- package/dist/edge/http/files.js +11 -7
- 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 +212 -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/project-templates.js +19 -2
- package/dist/edge/http/projects.js +37 -100
- package/dist/edge/http/prompts.js +40 -3
- package/dist/edge/http/pronunciations.js +23 -4
- package/dist/edge/http/providers.js +119 -3
- package/dist/edge/http/reviews.js +131 -0
- package/dist/edge/http/revision-files.js +3 -5
- package/dist/edge/http/revisions.js +1 -1
- package/dist/edge/http/run-cost.js +25 -0
- package/dist/edge/http/schedules.js +115 -6
- package/dist/edge/http/settings.js +84 -6
- package/dist/edge/http/storage-files.js +50 -0
- package/dist/edge/http/storage.js +43 -2
- package/dist/edge/http/studio.js +287 -0
- package/dist/edge/http/style-preview.js +61 -0
- package/dist/edge/http/subtitles.js +1 -1
- package/dist/edge/http/trash.js +69 -0
- package/dist/edge/http/tutorials.js +59 -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 +92 -0
- package/dist/edge/native-update.js +62 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/gemini-login.js +83 -0
- package/dist/host-cli/open-folder.js +10 -10
- package/dist/host-cli/runtime.js +9 -1
- package/dist/host-cli/status.js +4 -2
- 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/db/migrations/0041-cast-hosts.sql +3 -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 +10 -5
- package/dist/kernel/ports/narration-aliases.js +60 -0
- package/dist/kernel/ports/plan-limits.js +36 -0
- package/dist/kernel/ports/system-speech.js +203 -0
- package/dist/kernel/runner/attempt.js +17 -8
- package/dist/kernel/runner/audition.js +36 -0
- package/dist/kernel/runner/graph.js +40 -5
- package/dist/kernel/runner/index.js +39 -9
- 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 +277 -16
- package/dist/model-catalog.js +2 -0
- 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/3.0.1.md +105 -0
- package/dist/patch-notes/index.json +32 -0
- package/dist/slices/admission/model.js +27 -0
- package/dist/slices/admission/repo.js +24 -4
- package/dist/slices/admission/rules.js +80 -2
- package/dist/slices/admission/schema.js +58 -1
- package/dist/slices/admission/short-mode.js +47 -0
- package/dist/slices/admission/start.js +42 -6
- 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 +221 -0
- package/dist/slices/channels/runs.js +129 -0
- package/dist/slices/channels/schema.js +152 -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 +5 -2
- package/dist/slices/control/providers.js +4 -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 +68 -2
- package/dist/slices/eta/history.js +56 -0
- package/dist/slices/eta/model.js +63 -0
- package/dist/slices/eta/view.js +25 -0
- 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 +185 -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 +47 -3
- package/dist/slices/notifications/rules.js +48 -3
- package/dist/slices/onboarding/copy.js +143 -0
- package/dist/slices/onboarding/first-run.js +72 -0
- package/dist/slices/onboarding/full-video.js +81 -0
- package/dist/slices/onboarding/install.js +119 -0
- package/dist/slices/onboarding/model.js +85 -0
- package/dist/slices/onboarding/packs.js +491 -0
- package/dist/slices/onboarding/quick-short.js +144 -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 +127 -8
- package/dist/slices/play-drafts/readiness.js +4 -1
- 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 +59 -6
- package/dist/slices/project-templates/next-chapter.js +94 -0
- package/dist/slices/project-templates/one-off.js +26 -0
- package/dist/slices/project-templates/repo.js +2 -1
- package/dist/slices/project-templates/schema.js +7 -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 +67 -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 +125 -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 +267 -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 +62 -11
- 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/service-readiness.js +3 -3
- 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 +118 -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 +256 -0
- package/dist/slices/run-cost/pricing.js +95 -0
- package/dist/slices/run-cost/week.js +108 -0
- package/dist/slices/schedules/agenda.js +222 -0
- package/dist/slices/schedules/repo.js +58 -18
- package/dist/slices/schedules/scheduler.js +68 -17
- package/dist/slices/schedules/schema.js +179 -1
- package/dist/slices/schedules/service.js +43 -4
- package/dist/slices/schedules/similar.js +128 -0
- package/dist/slices/schedules/topic-list.js +163 -0
- package/dist/slices/schedules/topics.js +523 -0
- package/dist/slices/settings/first-run.js +106 -0
- package/dist/slices/settings/health.js +247 -0
- package/dist/slices/settings/key-guides.js +138 -0
- package/dist/slices/settings/key-test.js +93 -0
- package/dist/slices/settings/keys.js +10 -5
- package/dist/slices/settings/model-reach.js +52 -0
- package/dist/slices/settings/model.js +55 -1
- package/dist/slices/settings/models.js +5 -1
- package/dist/slices/settings/playback.js +15 -0
- package/dist/slices/settings/readiness.js +14 -2
- package/dist/slices/settings/repo.js +17 -3
- package/dist/slices/settings/tutorial-schema.js +5 -0
- 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 +63 -13
- 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 +87 -0
- package/dist/slices/storage/downloads.js +33 -1
- 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 +94 -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 +30 -0
- package/dist/slices/studio/pack.js +211 -0
- package/dist/slices/studio/queue.js +69 -0
- package/dist/slices/studio/settings.js +170 -0
- package/dist/slices/style-preview/images.js +61 -0
- package/dist/slices/style-preview/narration.js +33 -0
- package/dist/slices/style-preview/render.js +148 -0
- package/dist/slices/style-preview/schema.js +59 -0
- package/dist/slices/style-preview/service.js +92 -0
- package/dist/slices/style-preview/settings.js +139 -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/tutorials/anchors.js +57 -0
- package/dist/slices/tutorials/library.js +185 -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 +87 -0
- package/dist/slices/voices/audition.js +25 -0
- package/dist/slices/voices/cast.js +74 -0
- package/dist/slices/voices/grouping.js +59 -0
- package/dist/slices/voices/join.js +35 -0
- package/dist/slices/voices/languages.js +25 -0
- package/dist/slices/voices/model.js +218 -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 +84 -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/tutorials/AI-CLIs.md +146 -0
- package/dist/tutorials/Backups.md +121 -0
- package/dist/tutorials/Calendar.md +103 -0
- package/dist/tutorials/Cast-Library.md +112 -0
- package/dist/tutorials/Channels.md +148 -0
- package/dist/tutorials/Costs-and-Run-Cost.md +136 -0
- package/dist/tutorials/Docker.md +171 -0
- package/dist/tutorials/Document-Themes.md +207 -0
- package/dist/tutorials/Editing-a-Project.md +204 -0
- package/dist/tutorials/FAQ.md +134 -0
- package/dist/tutorials/First-Launch-and-Welcome.md +153 -0
- package/dist/tutorials/Home-and-Projects.md +174 -0
- package/dist/tutorials/Home.md +119 -0
- package/dist/tutorials/Install.md +154 -0
- package/dist/tutorials/Intros-and-Outros.md +86 -0
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +215 -0
- package/dist/tutorials/Library-Overview.md +75 -0
- package/dist/tutorials/Models.md +133 -0
- package/dist/tutorials/Multiple-Voices.md +161 -0
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +129 -0
- package/dist/tutorials/Notifications.md +86 -0
- package/dist/tutorials/Other-Languages.md +110 -0
- package/dist/tutorials/PDF-Documents.md +100 -0
- package/dist/tutorials/Play-Images.md +135 -0
- package/dist/tutorials/Play-Narration.md +167 -0
- package/dist/tutorials/Play-Outputs.md +109 -0
- package/dist/tutorials/Play-Overview.md +216 -0
- package/dist/tutorials/Play-Title-and-Article.md +135 -0
- package/dist/tutorials/Play-Video-and-Style.md +143 -0
- package/dist/tutorials/Project-Page.md +228 -0
- package/dist/tutorials/Prompts.md +136 -0
- package/dist/tutorials/Providers-and-Keys.md +290 -0
- package/dist/tutorials/Publishing-to-YouTube.md +135 -0
- package/dist/tutorials/Recovery-and-Retries.md +141 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +143 -0
- package/dist/tutorials/Schedules.md +154 -0
- package/dist/tutorials/Settings-Reference.md +258 -0
- package/dist/tutorials/Shorts.md +116 -0
- package/dist/tutorials/Start-at-Login.md +126 -0
- package/dist/tutorials/Studio-Extension.md +146 -0
- package/dist/tutorials/Templates.md +146 -0
- package/dist/tutorials/Trash.md +87 -0
- package/dist/tutorials/Troubleshooting.md +149 -0
- package/dist/tutorials/Uninstalling-and-Moving.md +113 -0
- package/dist/tutorials/Updating-and-Patch-Notes.md +120 -0
- package/dist/tutorials/Video-Editing.md +135 -0
- package/dist/tutorials/Where-Your-Files-Live.md +149 -0
- package/dist/tutorials/YouTube-Description.md +132 -0
- package/dist/tutorials/Your-First-Short.md +125 -0
- package/dist/tutorials/_Footer.md +1 -0
- package/dist/tutorials/_Sidebar.md +69 -0
- package/dist/updater/service.js +66 -21
- package/dist/web/assets/index-DBLSfqA-.css +1 -0
- package/dist/web/assets/index-DFdIhmm0.js +307 -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-B9OY1ioX.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +5 -5
- 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,492 @@
|
|
|
1
|
+
import { remark } from "remark";
|
|
2
|
+
import remarkGfm from "remark-gfm";
|
|
3
|
+
import { runFfmpeg } from "./ffmpeg.js";
|
|
4
|
+
// The 3.0 design tokens (`packages/web/src/styles/index.css`): the surface graphite, ink,
|
|
5
|
+
// the second ink, Slopify lime and the line colour, in the bundled Barlow.
|
|
6
|
+
export const defaultCardStyle = {
|
|
7
|
+
ground: "#19191c",
|
|
8
|
+
ink: "#ece9e2",
|
|
9
|
+
muted: "#a9a69e",
|
|
10
|
+
accent: "#a6d45c",
|
|
11
|
+
line: "#2a2a30",
|
|
12
|
+
font: "Barlow",
|
|
13
|
+
mono: "DejaVu Sans Mono",
|
|
14
|
+
};
|
|
15
|
+
// ceiling: the owner's eye at 1080p. Text never smaller than 2.4% of the short side (26 px on
|
|
16
|
+
// a 1080-line frame), so a card stays readable on a phone; a table with more rows than fit at
|
|
17
|
+
// that size says how many it left out.
|
|
18
|
+
const minShare = 0.024;
|
|
19
|
+
const rowSpacing = 1.55;
|
|
20
|
+
const lineSpacing = 1.35;
|
|
21
|
+
// Barlow's average advance is about half its size; a monospace face's is 0.6.
|
|
22
|
+
const textAdvance = 0.52;
|
|
23
|
+
const monoAdvance = 0.6;
|
|
24
|
+
export function cardLayout(input) {
|
|
25
|
+
const { width, height } = input;
|
|
26
|
+
const short = Math.min(width, height);
|
|
27
|
+
const margin = Math.round(short * 0.07);
|
|
28
|
+
const events = [];
|
|
29
|
+
let top = margin;
|
|
30
|
+
const label = labelOf(input);
|
|
31
|
+
if (label !== "") {
|
|
32
|
+
const size = Math.round(short * 0.03);
|
|
33
|
+
events.push(text(margin, top, 7, `{\\fs${String(size)}\\c${colour(input.style.accent)}}${assText(label)}`));
|
|
34
|
+
top += Math.round(size * 1.9);
|
|
35
|
+
}
|
|
36
|
+
const area = { x: margin, y: top, width: width - margin * 2, height: height - top - margin };
|
|
37
|
+
let box;
|
|
38
|
+
switch (input.kind) {
|
|
39
|
+
case "table":
|
|
40
|
+
events.push(...tableEvents(input, area));
|
|
41
|
+
break;
|
|
42
|
+
case "figure": {
|
|
43
|
+
const caption = figureCaption(input.source);
|
|
44
|
+
const size = Math.round(short * 0.032);
|
|
45
|
+
const lines = wrap(caption, Math.floor(area.width / (size * textAdvance)), 3);
|
|
46
|
+
const captionHeight = lines.length === 0 ? 0 : Math.round(lines.length * size * lineSpacing);
|
|
47
|
+
if (input.picture) {
|
|
48
|
+
box = {
|
|
49
|
+
x: area.x,
|
|
50
|
+
y: area.y,
|
|
51
|
+
width: area.width,
|
|
52
|
+
height: Math.max(1, area.height - captionHeight - (lines.length === 0 ? 0 : size)),
|
|
53
|
+
};
|
|
54
|
+
for (const [at, line] of lines.entries())
|
|
55
|
+
events.push(text(width / 2, area.y + area.height - captionHeight + at * size * lineSpacing, 8, `{\\fs${String(size)}\\c${colour(input.style.muted)}}${assText(line)}`));
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// No picture to show: the caption itself, large, as the figure's card.
|
|
59
|
+
const big = Math.round(short * 0.05);
|
|
60
|
+
const said = wrap(caption || "Figure", Math.floor(area.width / (big * textAdvance)), 6);
|
|
61
|
+
const start = area.y + (area.height - said.length * big * lineSpacing) / 2;
|
|
62
|
+
for (const [at, line] of said.entries())
|
|
63
|
+
events.push(text(width / 2, start + at * big * lineSpacing, 8, `{\\fs${String(big)}\\c${colour(input.style.ink)}}${assText(line)}`));
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
case "math":
|
|
68
|
+
events.push(...mathEvents(input, area));
|
|
69
|
+
break;
|
|
70
|
+
case "code":
|
|
71
|
+
case "diagram":
|
|
72
|
+
events.push(...codeEvents(input, area));
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
return { ass: script(input, events), ...(box === undefined ? {} : { box }) };
|
|
76
|
+
}
|
|
77
|
+
// The ffmpeg arguments that draw the card to `output` (a PNG) in `cwd`, which holds
|
|
78
|
+
// `card.ass` and a `fonts` folder.
|
|
79
|
+
export function cardArgs(input, layout, output, picture) {
|
|
80
|
+
const ground = `color=c=0x${input.style.ground.slice(1)}:s=${String(input.width)}x${String(input.height)}:d=1`;
|
|
81
|
+
const box = layout.box;
|
|
82
|
+
if (picture === undefined || box === undefined)
|
|
83
|
+
return [
|
|
84
|
+
...["-hide_banner", "-nostdin", "-loglevel", "error", "-y", "-f", "lavfi", "-i", ground],
|
|
85
|
+
...["-vf", "ass=filename=card.ass:fontsdir=fonts", "-frames:v", "1", output],
|
|
86
|
+
];
|
|
87
|
+
return [
|
|
88
|
+
...[
|
|
89
|
+
"-hide_banner",
|
|
90
|
+
"-nostdin",
|
|
91
|
+
"-loglevel",
|
|
92
|
+
"error",
|
|
93
|
+
"-y",
|
|
94
|
+
"-f",
|
|
95
|
+
"lavfi",
|
|
96
|
+
"-i",
|
|
97
|
+
ground,
|
|
98
|
+
"-i",
|
|
99
|
+
picture,
|
|
100
|
+
],
|
|
101
|
+
"-filter_complex",
|
|
102
|
+
[
|
|
103
|
+
`[1:v]scale=${String(box.width)}:${String(box.height)}:force_original_aspect_ratio=decrease:flags=lanczos,format=rgba[pic]`,
|
|
104
|
+
`[0:v][pic]overlay=x=${String(box.x)}+(${String(box.width)}-overlay_w)/2:y=${String(box.y)}+(${String(box.height)}-overlay_h)/2:format=auto,ass=filename=card.ass:fontsdir=fonts[v]`,
|
|
105
|
+
].join(";"),
|
|
106
|
+
...["-map", "[v]", "-frames:v", "1", output],
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
export async function renderCard(run, write) {
|
|
110
|
+
const layout = cardLayout(run.input);
|
|
111
|
+
write(layout.ass);
|
|
112
|
+
await runFfmpeg({
|
|
113
|
+
bin: run.bin,
|
|
114
|
+
args: cardArgs(run.input, layout, run.output, run.picture),
|
|
115
|
+
cwd: run.cwd,
|
|
116
|
+
signal: run.signal,
|
|
117
|
+
log: run.log,
|
|
118
|
+
onProgress: () => undefined,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function labelOf(input) {
|
|
122
|
+
const section = input.section?.trim() ?? "";
|
|
123
|
+
if (input.kind === "code") {
|
|
124
|
+
const lang = /^```\s*([\w+#.-]+)/.exec(input.source)?.[1];
|
|
125
|
+
return [section, lang].filter((part) => part !== undefined && part !== "").join(" · ");
|
|
126
|
+
}
|
|
127
|
+
return section;
|
|
128
|
+
}
|
|
129
|
+
// A table as a clean grid: the header in the accent colour over a rule, a hairline between
|
|
130
|
+
// rows, numbers right-aligned. Sized to fit; past the smallest readable size the rows that do
|
|
131
|
+
// not fit are left out and counted.
|
|
132
|
+
function tableEvents(input, area) {
|
|
133
|
+
const rows = tableRows(input.source);
|
|
134
|
+
const header = rows[0] ?? [];
|
|
135
|
+
const body = rows.slice(1);
|
|
136
|
+
const columns = Math.max(1, ...rows.map((row) => row.length));
|
|
137
|
+
const short = Math.min(input.width, input.height);
|
|
138
|
+
const smallest = Math.round(short * minShare);
|
|
139
|
+
const largest = Math.round(short * 0.045);
|
|
140
|
+
// Every row but the header, and a line for what was left out when some are.
|
|
141
|
+
let shown = body.length;
|
|
142
|
+
let size = Math.floor(area.height / ((shown + 1) * rowSpacing));
|
|
143
|
+
if (size < smallest) {
|
|
144
|
+
size = smallest;
|
|
145
|
+
shown = Math.max(1, Math.floor(area.height / (size * rowSpacing)) - 2);
|
|
146
|
+
}
|
|
147
|
+
size = Math.min(size, largest);
|
|
148
|
+
const cells = (row) => Array.from({ length: columns }, (_value, at) => row[at] ?? "");
|
|
149
|
+
const kept = [cells(header), ...body.slice(0, shown).map(cells)];
|
|
150
|
+
// Each column as wide as its widest cell (at most 28 letters); cut to fit if need be.
|
|
151
|
+
const letters = Array.from({ length: columns }, (_value, at) => Math.min(28, Math.max(3, ...kept.map((row) => (row[at] ?? "").length))));
|
|
152
|
+
const gap = 1.6;
|
|
153
|
+
const wanted = (letters.reduce((sum, n) => sum + n, 0) + gap * columns) * size * textAdvance;
|
|
154
|
+
if (wanted > area.width)
|
|
155
|
+
size = Math.max(smallest, Math.floor((size * area.width) / wanted));
|
|
156
|
+
const scale = area.width /
|
|
157
|
+
Math.max(area.width, (letters.reduce((sum, n) => sum + n, 0) + gap * columns) * size * textAdvance);
|
|
158
|
+
const widths = letters.map((n) => (n + gap) * size * textAdvance * scale);
|
|
159
|
+
const room = (at) => Math.max(3, Math.floor((widths[at] ?? 0) / (size * textAdvance)) - 1);
|
|
160
|
+
const rowHeight = Math.round(size * rowSpacing);
|
|
161
|
+
const events = [];
|
|
162
|
+
// Centred across the card; a narrow table is not pushed into a corner.
|
|
163
|
+
const tableWidth = widths.reduce((sum, n) => sum + n, 0);
|
|
164
|
+
const left = area.x + Math.max(0, (area.width - tableWidth) / 2);
|
|
165
|
+
const right = left + tableWidth;
|
|
166
|
+
kept.forEach((row, index) => {
|
|
167
|
+
const y = area.y + index * rowHeight;
|
|
168
|
+
let x = left;
|
|
169
|
+
row.forEach((cell, at) => {
|
|
170
|
+
const value = clip(cell, room(at));
|
|
171
|
+
const numeric = index > 0 && /^[-+−]?[\d.,]+\s*%?$|^[-+−]?[\d.,]+\s*[a-zA-Zµ%/]{0,4}$/.test(cell.trim());
|
|
172
|
+
const cellWidth = widths[at] ?? 0;
|
|
173
|
+
const colourTag = index === 0 ? colour(input.style.accent) : colour(input.style.ink);
|
|
174
|
+
events.push(numeric
|
|
175
|
+
? text(x + cellWidth - size * 0.8, y + rowHeight / 2, 6, `{\\fs${String(size)}\\c${colourTag}}${assText(value)}`)
|
|
176
|
+
: text(x, y + rowHeight / 2, 4, `{\\fs${String(size)}\\c${colourTag}${index === 0 ? "\\b1" : ""}}${assText(value)}`));
|
|
177
|
+
x += cellWidth;
|
|
178
|
+
});
|
|
179
|
+
// The rule under the header is the accent's; the rest are hairlines.
|
|
180
|
+
const under = y + rowHeight;
|
|
181
|
+
const thick = index === 0 ? Math.max(2, Math.round(size / 10)) : 1;
|
|
182
|
+
events.push(rule(left, under, right - left, thick, index === 0 ? input.style.accent : input.style.line));
|
|
183
|
+
});
|
|
184
|
+
const leftOut = body.length - Math.min(shown, body.length);
|
|
185
|
+
if (leftOut > 0)
|
|
186
|
+
events.push(text(left, area.y + kept.length * rowHeight + rowHeight / 2, 4, `{\\fs${String(size)}\\c${colour(input.style.muted)}\\i1}${assText(`…and ${String(leftOut)} more ${leftOut === 1 ? "row" : "rows"}`)}`));
|
|
187
|
+
return events;
|
|
188
|
+
}
|
|
189
|
+
function tableRows(markdown) {
|
|
190
|
+
const tree = remark().use(remarkGfm).parse(markdown);
|
|
191
|
+
const table = tree.children.find((node) => node.type === "table");
|
|
192
|
+
if (table === undefined)
|
|
193
|
+
return [];
|
|
194
|
+
return table.children.map((row) => row.children.map((cell) => plain(cell).trim()));
|
|
195
|
+
}
|
|
196
|
+
function plain(node) {
|
|
197
|
+
if ("value" in node && typeof node.value === "string")
|
|
198
|
+
return node.value;
|
|
199
|
+
if (node.type === "image")
|
|
200
|
+
return node.alt ?? "";
|
|
201
|
+
if ("children" in node)
|
|
202
|
+
return node.children.map((child) => plain(child)).join("");
|
|
203
|
+
return "";
|
|
204
|
+
}
|
|
205
|
+
// Code and drawings in the monospace face, one line per line, with simple colouring: comments
|
|
206
|
+
// muted, strings and numbers in the ink's warm second tone, keywords in the accent.
|
|
207
|
+
function codeEvents(input, area) {
|
|
208
|
+
const lines = fenced(input.source).split("\n");
|
|
209
|
+
const longest = Math.max(12, ...lines.map((line) => line.length));
|
|
210
|
+
const short = Math.min(input.width, input.height);
|
|
211
|
+
const smallest = Math.round(short * minShare * 0.9);
|
|
212
|
+
let size = Math.min(Math.round(short * 0.04), Math.floor(area.width / (longest * monoAdvance)), Math.floor(area.height / (lines.length * lineSpacing)));
|
|
213
|
+
let shown = lines.length;
|
|
214
|
+
if (size < smallest) {
|
|
215
|
+
size = smallest;
|
|
216
|
+
shown = Math.max(1, Math.floor(area.height / (size * lineSpacing)) - 1);
|
|
217
|
+
}
|
|
218
|
+
const room = Math.floor(area.width / (size * monoAdvance));
|
|
219
|
+
// A short program sits in the middle of the card rather than at its top.
|
|
220
|
+
const top = area.y + Math.max(0, (area.height - shown * size * lineSpacing) / 2);
|
|
221
|
+
const events = lines
|
|
222
|
+
.slice(0, shown)
|
|
223
|
+
.map((line, at) => text(area.x, top + at * size * lineSpacing, 7, `{\\fn${input.style.mono}\\fs${String(size)}\\c${colour(input.style.ink)}}${input.kind === "code"
|
|
224
|
+
? coloured(clip(line, room), input.style)
|
|
225
|
+
: assText(clip(line, room), true)}`));
|
|
226
|
+
if (shown < lines.length)
|
|
227
|
+
events.push(text(area.x, top + shown * size * lineSpacing, 7, `{\\fs${String(size)}\\c${colour(input.style.muted)}\\i1}${assText(`…and ${String(lines.length - shown)} more lines`)}`));
|
|
228
|
+
return events;
|
|
229
|
+
}
|
|
230
|
+
const keywords = new Set("fn let mut pub use mod impl struct enum trait match if else for while loop return break continue in as where const static async await function var class extends new this def import from export type interface yield lambda try catch except finally raise throw public private protected void int float double bool boolean char string true false null None nil self Self package func go defer select case switch default".split(" "));
|
|
231
|
+
function coloured(line, style) {
|
|
232
|
+
const comment = /(\/\/|#(?!\[)|--\s).*$/.exec(line);
|
|
233
|
+
const code = comment === null ? line : line.slice(0, comment.index);
|
|
234
|
+
const out = code.replace(/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\b\d[\d._]*\b|\b[A-Za-z_]\w*\b|[^"'\w]+)/g, (token) => {
|
|
235
|
+
if (/^["']/.test(token))
|
|
236
|
+
return `{\\c${colour(style.muted)}}${assText(token, true)}{\\c${colour(style.ink)}}`;
|
|
237
|
+
if (/^\d/.test(token))
|
|
238
|
+
return `{\\c${colour(style.muted)}}${assText(token, true)}{\\c${colour(style.ink)}}`;
|
|
239
|
+
if (keywords.has(token))
|
|
240
|
+
return `{\\c${colour(style.accent)}}${token}{\\c${colour(style.ink)}}`;
|
|
241
|
+
return assText(token, true);
|
|
242
|
+
});
|
|
243
|
+
return comment === null
|
|
244
|
+
? out
|
|
245
|
+
: `${out}{\\c${colour(style.muted)}\\i1}${assText(comment[0], true)}`;
|
|
246
|
+
}
|
|
247
|
+
// An equation set in readable text: TeX's commands as the symbols they stand for, powers
|
|
248
|
+
// and indices as superscripts and subscripts where Unicode has them, centred and large.
|
|
249
|
+
function mathEvents(input, area) {
|
|
250
|
+
const tex = input.source
|
|
251
|
+
.replace(/^\$\$|\$\$$/g, "")
|
|
252
|
+
.replace(/^\\\[|\\\]$/g, "")
|
|
253
|
+
.replace(/^```\s*\w*\n?|```$/g, "")
|
|
254
|
+
.trim();
|
|
255
|
+
// A paragraph with formulas in it shows the paragraph with its formulas set.
|
|
256
|
+
const said = /\$[^$]+\$/.test(tex)
|
|
257
|
+
? tex.replace(/\$\$?([^$]+)\$\$?/g, (_all, inner) => typeset(inner))
|
|
258
|
+
: typeset(tex);
|
|
259
|
+
const short = Math.min(input.width, input.height);
|
|
260
|
+
const size = Math.round(short * (said.length > 60 ? 0.04 : 0.06));
|
|
261
|
+
const lines = wrap(said, Math.floor(area.width / (size * textAdvance)), 6);
|
|
262
|
+
const start = area.y + (area.height - lines.length * size * lineSpacing) / 2;
|
|
263
|
+
return lines.map((line, at) => text(input.width / 2, start + at * size * lineSpacing, 8, `{\\fs${String(size)}\\c${colour(input.style.ink)}}${assText(line)}`));
|
|
264
|
+
}
|
|
265
|
+
const symbols = {
|
|
266
|
+
alpha: "α",
|
|
267
|
+
beta: "β",
|
|
268
|
+
gamma: "γ",
|
|
269
|
+
delta: "δ",
|
|
270
|
+
epsilon: "ε",
|
|
271
|
+
varepsilon: "ε",
|
|
272
|
+
zeta: "ζ",
|
|
273
|
+
eta: "η",
|
|
274
|
+
theta: "θ",
|
|
275
|
+
iota: "ι",
|
|
276
|
+
kappa: "κ",
|
|
277
|
+
lambda: "λ",
|
|
278
|
+
mu: "μ",
|
|
279
|
+
nu: "ν",
|
|
280
|
+
xi: "ξ",
|
|
281
|
+
pi: "π",
|
|
282
|
+
rho: "ρ",
|
|
283
|
+
sigma: "σ",
|
|
284
|
+
tau: "τ",
|
|
285
|
+
upsilon: "υ",
|
|
286
|
+
phi: "φ",
|
|
287
|
+
varphi: "φ",
|
|
288
|
+
chi: "χ",
|
|
289
|
+
psi: "ψ",
|
|
290
|
+
omega: "ω",
|
|
291
|
+
Gamma: "Γ",
|
|
292
|
+
Delta: "Δ",
|
|
293
|
+
Theta: "Θ",
|
|
294
|
+
Lambda: "Λ",
|
|
295
|
+
Xi: "Ξ",
|
|
296
|
+
Pi: "Π",
|
|
297
|
+
Sigma: "Σ",
|
|
298
|
+
Phi: "Φ",
|
|
299
|
+
Psi: "Ψ",
|
|
300
|
+
Omega: "Ω",
|
|
301
|
+
cdot: "·",
|
|
302
|
+
times: "×",
|
|
303
|
+
div: "÷",
|
|
304
|
+
pm: "±",
|
|
305
|
+
mp: "∓",
|
|
306
|
+
le: "≤",
|
|
307
|
+
leq: "≤",
|
|
308
|
+
ge: "≥",
|
|
309
|
+
geq: "≥",
|
|
310
|
+
neq: "≠",
|
|
311
|
+
ne: "≠",
|
|
312
|
+
approx: "≈",
|
|
313
|
+
sim: "∼",
|
|
314
|
+
equiv: "≡",
|
|
315
|
+
propto: "∝",
|
|
316
|
+
infty: "∞",
|
|
317
|
+
partial: "∂",
|
|
318
|
+
nabla: "∇",
|
|
319
|
+
sum: "Σ",
|
|
320
|
+
prod: "Π",
|
|
321
|
+
int: "∫",
|
|
322
|
+
to: "→",
|
|
323
|
+
rightarrow: "→",
|
|
324
|
+
leftarrow: "←",
|
|
325
|
+
Rightarrow: "⇒",
|
|
326
|
+
in: "∈",
|
|
327
|
+
forall: "∀",
|
|
328
|
+
exists: "∃",
|
|
329
|
+
ldots: "…",
|
|
330
|
+
cdots: "⋯",
|
|
331
|
+
degree: "°",
|
|
332
|
+
circ: "°",
|
|
333
|
+
};
|
|
334
|
+
const superscripts = {
|
|
335
|
+
"0": "⁰",
|
|
336
|
+
"1": "¹",
|
|
337
|
+
"2": "²",
|
|
338
|
+
"3": "³",
|
|
339
|
+
"4": "⁴",
|
|
340
|
+
"5": "⁵",
|
|
341
|
+
"6": "⁶",
|
|
342
|
+
"7": "⁷",
|
|
343
|
+
"8": "⁸",
|
|
344
|
+
"9": "⁹",
|
|
345
|
+
"+": "⁺",
|
|
346
|
+
"-": "⁻",
|
|
347
|
+
"=": "⁼",
|
|
348
|
+
"(": "⁽",
|
|
349
|
+
")": "⁾",
|
|
350
|
+
n: "ⁿ",
|
|
351
|
+
i: "ⁱ",
|
|
352
|
+
};
|
|
353
|
+
const subscripts = {
|
|
354
|
+
"0": "₀",
|
|
355
|
+
"1": "₁",
|
|
356
|
+
"2": "₂",
|
|
357
|
+
"3": "₃",
|
|
358
|
+
"4": "₄",
|
|
359
|
+
"5": "₅",
|
|
360
|
+
"6": "₆",
|
|
361
|
+
"7": "₇",
|
|
362
|
+
"8": "₈",
|
|
363
|
+
"9": "₉",
|
|
364
|
+
"+": "₊",
|
|
365
|
+
"-": "₋",
|
|
366
|
+
"=": "₌",
|
|
367
|
+
"(": "₍",
|
|
368
|
+
")": "₎",
|
|
369
|
+
a: "ₐ",
|
|
370
|
+
e: "ₑ",
|
|
371
|
+
o: "ₒ",
|
|
372
|
+
x: "ₓ",
|
|
373
|
+
i: "ᵢ",
|
|
374
|
+
j: "ⱼ",
|
|
375
|
+
k: "ₖ",
|
|
376
|
+
n: "ₙ",
|
|
377
|
+
t: "ₜ",
|
|
378
|
+
s: "ₛ",
|
|
379
|
+
r: "ᵣ",
|
|
380
|
+
m: "ₘ",
|
|
381
|
+
p: "ₚ",
|
|
382
|
+
};
|
|
383
|
+
export function typeset(tex) {
|
|
384
|
+
let out = tex
|
|
385
|
+
.replace(/\\(left|right|big|Big|bigg|Bigg|displaystyle|,|;|!|quad|qquad)\b/g, " ")
|
|
386
|
+
.replace(/\\(mathrm|mathbf|mathit|text|operatorname)\{([^}]*)\}/g, "$2");
|
|
387
|
+
// Innermost first, so a fraction inside a root comes out whole.
|
|
388
|
+
for (let pass = 0; pass < 4; pass++)
|
|
389
|
+
out = out
|
|
390
|
+
.replace(/\\frac\{([^{}]*)\}\{([^{}]*)\}/g, (_all, a, b) => `${group(a)}/${group(b)}`)
|
|
391
|
+
.replace(/\\sqrt\{([^{}]*)\}/g, (_all, a) => `√${group(a)}`);
|
|
392
|
+
out = out
|
|
393
|
+
.replace(/\\([A-Za-z]+)/g, (_all, name) => symbols[name] ?? name)
|
|
394
|
+
.replace(/\^\{([^{}]*)\}|\^(\S)/g, (_all, a, b) => scripted(a ?? b ?? "", superscripts, "^"))
|
|
395
|
+
.replace(/_\{([^{}]*)\}|_(\S)/g, (_all, a, b) => scripted(a ?? b ?? "", subscripts, "_"))
|
|
396
|
+
.replace(/[{}]/g, "")
|
|
397
|
+
.replace(/\s*([=<>≤≥≈≠+×·−])\s*/g, " $1 ")
|
|
398
|
+
.replace(/\s+/g, " ")
|
|
399
|
+
.trim();
|
|
400
|
+
return out;
|
|
401
|
+
}
|
|
402
|
+
function group(text) {
|
|
403
|
+
const clean = text.trim();
|
|
404
|
+
return /^[\w.]+$/.test(clean) ? clean : `(${clean})`;
|
|
405
|
+
}
|
|
406
|
+
function scripted(text, map, mark) {
|
|
407
|
+
const letters = [...text];
|
|
408
|
+
return letters.every((letter) => map[letter] !== undefined)
|
|
409
|
+
? letters.map((letter) => map[letter]).join("")
|
|
410
|
+
: `${mark}(${text})`;
|
|
411
|
+
}
|
|
412
|
+
function figureCaption(source) {
|
|
413
|
+
const lines = source.split("\n");
|
|
414
|
+
const captions = lines
|
|
415
|
+
.filter((line) => line.startsWith("Caption: "))
|
|
416
|
+
.map((line) => line.slice(9));
|
|
417
|
+
const alt = lines.find((line) => line.startsWith("Alt text: "))?.slice(10);
|
|
418
|
+
return (captions.length > 0 ? captions.join(" ") : (alt ?? "")).trim();
|
|
419
|
+
}
|
|
420
|
+
function fenced(source) {
|
|
421
|
+
return source
|
|
422
|
+
.replace(/^\s*(```|~~~)[^\n]*\n?/, "")
|
|
423
|
+
.replace(/\n?(```|~~~)\s*$/, "")
|
|
424
|
+
.replace(/\t/g, " ")
|
|
425
|
+
.replace(/\s+$/, "");
|
|
426
|
+
}
|
|
427
|
+
function wrap(textValue, width, most) {
|
|
428
|
+
const words = textValue.split(/\s+/).filter(Boolean);
|
|
429
|
+
const lines = [];
|
|
430
|
+
let line = "";
|
|
431
|
+
for (const word of words) {
|
|
432
|
+
if (line === "")
|
|
433
|
+
line = word;
|
|
434
|
+
else if (line.length + 1 + word.length <= width)
|
|
435
|
+
line += ` ${word}`;
|
|
436
|
+
else {
|
|
437
|
+
lines.push(line);
|
|
438
|
+
line = word;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (line !== "")
|
|
442
|
+
lines.push(line);
|
|
443
|
+
if (lines.length <= most)
|
|
444
|
+
return lines;
|
|
445
|
+
const kept = lines.slice(0, most);
|
|
446
|
+
kept[most - 1] = clip(`${kept[most - 1] ?? ""} …`, width);
|
|
447
|
+
return kept;
|
|
448
|
+
}
|
|
449
|
+
function clip(value, room) {
|
|
450
|
+
return value.length <= room ? value : `${value.slice(0, Math.max(1, room - 1))}…`;
|
|
451
|
+
}
|
|
452
|
+
// Text as libass draws it literally: braces and backslashes escaped rather than read as
|
|
453
|
+
// override tags; code keeps its spaces, prose has them collapsed.
|
|
454
|
+
function assText(value, keepSpaces = false) {
|
|
455
|
+
const escaped = value.replace(/\\/g, "\\\\").replace(/\{/g, "\\{").replace(/\}/g, "\\}");
|
|
456
|
+
return keepSpaces
|
|
457
|
+
? escaped.replace(/ {2,}/g, (run) => "\\h".repeat(run.length))
|
|
458
|
+
: escaped.replace(/\s+/g, " ");
|
|
459
|
+
}
|
|
460
|
+
function colour(hex) {
|
|
461
|
+
const match = /^#([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/.exec(hex);
|
|
462
|
+
if (match === null)
|
|
463
|
+
return "&HFFFFFF&";
|
|
464
|
+
const [, r, g, b] = match;
|
|
465
|
+
return `&H${(b ?? "").toUpperCase()}${(g ?? "").toUpperCase()}${(r ?? "").toUpperCase()}&`;
|
|
466
|
+
}
|
|
467
|
+
function text(x, y, align, body) {
|
|
468
|
+
return `Dialogue: 0,0:00:00.00,0:00:10.00,Card,,0,0,0,,{\\an${String(align)}\\pos(${String(Math.round(x))},${String(Math.round(y))})}${body}`;
|
|
469
|
+
}
|
|
470
|
+
function rule(x, y, width, thick, hex) {
|
|
471
|
+
const [x0, y0, x1, y1] = [x, y, x + width, y + thick].map((n) => String(Math.round(n)));
|
|
472
|
+
return `Dialogue: 0,0:00:00.00,0:00:10.00,Card,,0,0,0,,{\\an7\\pos(0,0)\\p1\\bord0\\shad0\\c${colour(hex)}}m ${x0} ${y0} l ${x1} ${y0} l ${x1} ${y1} l ${x0} ${y1}{\\p0}`;
|
|
473
|
+
}
|
|
474
|
+
function script(input, events) {
|
|
475
|
+
const { width, height, style } = input;
|
|
476
|
+
return `${[
|
|
477
|
+
"[Script Info]",
|
|
478
|
+
"ScriptType: v4.00+",
|
|
479
|
+
`PlayResX: ${String(width)}`,
|
|
480
|
+
`PlayResY: ${String(height)}`,
|
|
481
|
+
"WrapStyle: 2",
|
|
482
|
+
"ScaledBorderAndShadow: yes",
|
|
483
|
+
"",
|
|
484
|
+
"[V4+ Styles]",
|
|
485
|
+
"Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding",
|
|
486
|
+
`Style: Card,${style.font.replace(/,/g, " ")},${String(Math.round(Math.min(width, height) * 0.03))},&H00FFFFFF,&H00FFFFFF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,0,0,7,0,0,0,1`,
|
|
487
|
+
"",
|
|
488
|
+
"[Events]",
|
|
489
|
+
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text",
|
|
490
|
+
...events,
|
|
491
|
+
].join("\n")}\n`;
|
|
492
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// ceiling: the words compared at each end of a passage, and how many of them must match.
|
|
2
|
+
const edgeWords = 4;
|
|
3
|
+
const matchShare = 0.75;
|
|
4
|
+
export function passageSpans(words, passages) {
|
|
5
|
+
const flat = [];
|
|
6
|
+
for (const [index, word] of words.entries())
|
|
7
|
+
for (const token of tokens(word.text))
|
|
8
|
+
flat.push({ token, word: index });
|
|
9
|
+
let cursor = 0;
|
|
10
|
+
return passages.map((passage) => {
|
|
11
|
+
const wanted = tokens(passage);
|
|
12
|
+
if (wanted.length === 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
const k = Math.min(edgeWords, wanted.length);
|
|
15
|
+
const head = wanted.slice(0, k);
|
|
16
|
+
const start = find(flat, head, cursor, flat.length);
|
|
17
|
+
if (start === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
const tail = wanted.slice(-k);
|
|
20
|
+
const from = start + Math.max(0, Math.floor(wanted.length / 2) - k);
|
|
21
|
+
const to = Math.min(flat.length, start + Math.ceil(wanted.length * 1.5) + k);
|
|
22
|
+
const last = find(flat, tail, from, to);
|
|
23
|
+
const endToken = Math.min(flat.length - 1, last === undefined ? start + wanted.length - 1 : last + k - 1);
|
|
24
|
+
cursor = endToken + 1;
|
|
25
|
+
const first = words[flat[start]?.word ?? 0];
|
|
26
|
+
const final = words[flat[endToken]?.word ?? 0];
|
|
27
|
+
return first === undefined || final === undefined
|
|
28
|
+
? undefined
|
|
29
|
+
: { start: first.start, end: Math.max(first.start, final.end) };
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function find(flat, wanted, from, to) {
|
|
33
|
+
const need = Math.ceil(wanted.length * matchShare);
|
|
34
|
+
for (let at = from; at + wanted.length <= Math.max(to, from + wanted.length) && at + wanted.length <= flat.length; at++) {
|
|
35
|
+
let hits = 0;
|
|
36
|
+
for (const [offset, token] of wanted.entries())
|
|
37
|
+
if (flat[at + offset]?.token === token)
|
|
38
|
+
hits += 1;
|
|
39
|
+
if (hits >= need)
|
|
40
|
+
return at;
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
function tokens(text) {
|
|
45
|
+
return text
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.normalize("NFKD")
|
|
48
|
+
.replace(/\p{M}/gu, "")
|
|
49
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
}
|