@gentbajko/slopify 3.25.9 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/account-limits.js +30 -0
- package/dist/adapters/alignment/audio.js +1 -1
- package/dist/adapters/alignment/cache.js +4 -6
- package/dist/adapters/alignment/ctc.js +1 -2
- package/dist/adapters/alignment/lock.js +4 -3
- package/dist/adapters/alignment/multilingual.js +2 -4
- package/dist/adapters/alignment/prefetch.js +2 -1
- package/dist/adapters/alignment/runner.js +1 -1
- package/dist/adapters/alignment/worker.js +3 -2
- package/dist/adapters/explain.js +10 -12
- package/dist/adapters/ffmpeg.js +19 -1
- package/dist/adapters/host-cli/index.js +21 -4
- package/dist/adapters/host-cli/transport.js +2 -1
- package/dist/adapters/http-failure.js +53 -0
- package/dist/adapters/image/bytes.js +2 -1
- package/dist/adapters/image/codex.js +42 -9
- package/dist/adapters/image/fal.js +21 -53
- package/dist/adapters/image/google.js +33 -56
- package/dist/adapters/image/models.js +2 -2
- package/dist/adapters/image/openai.js +22 -48
- package/dist/adapters/image/reference.js +36 -0
- package/dist/adapters/image/replicate.js +21 -49
- package/dist/adapters/image/video.js +2 -1
- package/dist/adapters/interest/search-interest.js +113 -0
- package/dist/adapters/key-probes.js +54 -3
- package/dist/adapters/llm/antigravity-models.js +1 -1
- package/dist/adapters/llm/antigravity.js +11 -6
- package/dist/adapters/llm/assistant-cli.js +388 -0
- package/dist/adapters/llm/child-session.js +83 -0
- package/dist/adapters/llm/claude-code-models.js +52 -99
- package/dist/adapters/llm/claude-code.js +16 -4
- package/dist/adapters/llm/codex-limits.js +27 -68
- package/dist/adapters/llm/codex-models.js +1 -1
- package/dist/adapters/llm/codex-server-models.js +61 -109
- package/dist/adapters/llm/codex.js +12 -1
- package/dist/adapters/llm/document-workspace.js +3 -2
- package/dist/adapters/llm/image-workspace.js +3 -2
- package/dist/adapters/llm/openrouter.js +12 -48
- package/dist/adapters/llm/run-cli.js +9 -24
- package/dist/adapters/llm/sse-lines.js +5 -1
- package/dist/adapters/model-access.js +37 -0
- package/dist/adapters/tts/cartesia.js +15 -46
- package/dist/adapters/tts/elevenlabs.js +20 -52
- package/dist/adapters/tts/gemini.js +21 -41
- package/dist/adapters/tts/inworld-async.js +6 -2
- package/dist/adapters/tts/inworld.js +6 -2
- package/dist/adapters/tts/openai.js +20 -50
- package/dist/adapters/tts/system.js +2 -1
- package/dist/assets/models-lifecycle.yaml +6 -0
- package/dist/assistant-wiring.js +60 -0
- package/dist/autopilot-timers.js +59 -0
- package/dist/catalog/lifecycle.js +52 -0
- package/dist/catalog/propose.js +204 -0
- package/dist/catalog/registry.js +23 -14
- package/dist/catalog/schema.js +4 -1
- package/dist/catalog/store.js +39 -0
- package/dist/catalog/validate.js +5 -2
- package/dist/edge/autostart/native.js +3 -5
- package/dist/edge/autostart/prompt.js +2 -1
- package/dist/edge/autostart/service.js +3 -4
- package/dist/edge/cli-args.js +3 -4
- package/dist/edge/cli.js +2 -1
- package/dist/edge/docker-install/apply.js +11 -11
- package/dist/edge/docker-install/run.js +2 -1
- package/dist/edge/docker-install/state.js +4 -3
- package/dist/edge/docker-install/tree.js +2 -4
- package/dist/edge/docker-install/volume.js +3 -2
- package/dist/edge/docker.js +5 -4
- package/dist/edge/events/hub.js +5 -5
- package/dist/edge/http/actions.js +11 -32
- package/dist/edge/http/actor.js +5 -3
- package/dist/edge/http/app.js +86 -11
- package/dist/edge/http/assistant.js +129 -0
- package/dist/edge/http/audio-preview.js +4 -11
- package/dist/edge/http/auditions.js +1 -1
- package/dist/edge/http/autostart.js +5 -5
- package/dist/edge/http/backups.js +2 -4
- package/dist/edge/http/channel-memory.js +9 -22
- package/dist/edge/http/channels.js +14 -27
- package/dist/edge/http/checkpoints.js +39 -21
- package/dist/edge/http/design-templates.js +167 -0
- package/dist/edge/http/designs.js +178 -0
- package/dist/edge/http/diagnostics.js +5 -1
- package/dist/edge/http/document-themes.js +5 -14
- package/dist/edge/http/draft-files.js +1 -3
- package/dist/edge/http/draft-problem.js +5 -10
- package/dist/edge/http/drafts.js +3 -3
- package/dist/edge/http/entries.js +6 -9
- package/dist/edge/http/extension-auth.js +27 -0
- package/dist/edge/http/external-reviews.js +12 -11
- package/dist/edge/http/files.js +5 -11
- package/dist/edge/http/folder-location.js +1 -4
- package/dist/edge/http/fonts.js +0 -7
- package/dist/edge/http/home.js +47 -14
- package/dist/edge/http/host-cli.js +18 -8
- package/dist/edge/http/ids.js +8 -0
- package/dist/edge/http/keywords.js +35 -0
- package/dist/edge/http/model-checks.js +132 -0
- package/dist/edge/http/narration-peaks.js +5 -13
- package/dist/edge/http/onboarding.js +30 -26
- package/dist/edge/http/open-folder.js +5 -10
- package/dist/edge/http/outputs.js +7 -14
- package/dist/edge/http/patch-notes.js +6 -12
- package/dist/edge/http/planning.js +3 -7
- package/dist/edge/http/problem.js +2 -4
- package/dist/edge/http/project-create.js +2 -1
- package/dist/edge/http/project-templates.js +31 -19
- package/dist/edge/http/projects.js +24 -16
- package/dist/edge/http/prompts.js +9 -16
- package/dist/edge/http/pronunciations.js +1 -2
- package/dist/edge/http/providers.js +48 -34
- package/dist/edge/http/refusal.js +29 -0
- package/dist/edge/http/remote-access.js +40 -0
- package/dist/edge/http/remote-guard.js +71 -0
- package/dist/edge/http/request-guard.js +91 -0
- package/dist/edge/http/reviews.js +12 -25
- package/dist/edge/http/revision-files.js +6 -11
- package/dist/edge/http/revisions.js +14 -19
- package/dist/edge/http/run-cost.js +1 -2
- package/dist/edge/http/same-page.js +80 -0
- package/dist/edge/http/schedules.js +69 -40
- package/dist/edge/http/settings.js +27 -14
- package/dist/edge/http/social.js +283 -0
- package/dist/edge/http/staging.js +3 -5
- package/dist/edge/http/storage-files.js +5 -7
- package/dist/edge/http/storage.js +33 -25
- package/dist/edge/http/studio.js +94 -69
- package/dist/edge/http/style-preview.js +3 -6
- package/dist/edge/http/subtitles.js +3 -17
- package/dist/edge/http/template-studio.js +436 -0
- package/dist/edge/http/trash.js +11 -12
- package/dist/edge/http/tutorial.js +1 -2
- package/dist/edge/http/tutorials.js +5 -11
- package/dist/edge/http/update.js +7 -13
- package/dist/edge/http/waveform.js +3 -6
- package/dist/edge/http/youtube-edits.js +3 -4
- package/dist/edge/mcp/assistant.js +279 -0
- package/dist/edge/mcp/attribution.js +8 -2
- package/dist/edge/mcp/call-route.js +11 -2
- package/dist/edge/mcp/excluded-routes.js +29 -3
- package/dist/edge/mcp/http.js +11 -2
- package/dist/edge/mcp/instructions.js +6 -4
- package/dist/edge/mcp/library-tools.js +9 -5
- package/dist/edge/mcp/remake-tools.js +7 -4
- package/dist/edge/mcp/route-tool.js +35 -15
- package/dist/edge/mcp/server.js +13 -8
- package/dist/edge/mcp/tools-channels.js +11 -9
- package/dist/edge/mcp/tools-designs.js +165 -0
- package/dist/edge/mcp/tools-library.js +19 -7
- package/dist/edge/mcp/tools-planning.js +40 -21
- package/dist/edge/mcp/tools-projects.js +38 -11
- package/dist/edge/mcp/tools-publishing.js +9 -9
- package/dist/edge/mcp/tools-runs.js +23 -10
- package/dist/edge/mcp/tools-schedules.js +32 -8
- package/dist/edge/mcp/tools-setup.js +75 -11
- package/dist/edge/mcp/tools-social.js +47 -0
- package/dist/edge/mcp/tools-storage.js +11 -3
- package/dist/edge/mcp/tools-template-studio.js +166 -0
- package/dist/edge/mcp/tools.js +6 -0
- package/dist/edge/remote-access/cli.js +71 -0
- package/dist/edge/remote-access/model.js +32 -0
- package/dist/edge/remote-access/rules.js +207 -0
- package/dist/edge/remote-access/service.js +203 -0
- package/dist/edge/remote-access/store.js +44 -0
- package/dist/edge/remote-access/views.js +43 -0
- package/dist/extension/slopify-studio-chrome.zip +0 -0
- package/dist/extension/slopify-studio-firefox.zip +0 -0
- package/dist/host-cli/install.js +2 -2
- package/dist/host-cli/open-folder.js +2 -2
- package/dist/host-cli/paths.js +2 -4
- package/dist/host-cli/server.js +4 -4
- package/dist/host-cli/service.js +3 -3
- package/dist/host-cli/status.js +7 -7
- package/dist/kernel/db/migrations/0063-model-checks.sql +20 -0
- package/dist/kernel/db/migrations/0064-template-studio.sql +46 -0
- package/dist/kernel/db/migrations/0065-promise-and-assistant-reviews.sql +58 -0
- package/dist/kernel/db/migrations/0066-assistant.sql +44 -0
- package/dist/kernel/db/migrations/0067-autopilot.sql +6 -0
- package/dist/kernel/db/migrations/0068-topic-scores.sql +17 -0
- package/dist/kernel/db/migrations/0070-designs.sql +53 -0
- package/dist/kernel/db/migrations/0071-design-usage.sql +33 -0
- package/dist/kernel/db/migrations/0072-design-templates.sql +33 -0
- package/dist/kernel/db/migrations/0073-schedule-design-kind.sql +5 -0
- package/dist/kernel/db/migrations/0074-template-bakeoffs.sql +44 -0
- package/dist/kernel/db/migrations/0075-social-posts.sql +25 -0
- package/dist/kernel/db/migrations/0076-channel-social.sql +5 -0
- package/dist/kernel/db/migrations/0077-post-prompts.sql +17 -0
- package/dist/kernel/db/migrations/0078-uploaded-from-links.sql +7 -0
- package/dist/kernel/db/tx.js +2 -4
- package/dist/kernel/errors.js +27 -1
- package/dist/kernel/json-answer.js +15 -0
- package/dist/kernel/lock.js +4 -9
- package/dist/kernel/paths.js +8 -0
- package/dist/kernel/pipeline.js +12 -0
- package/dist/kernel/ports/assistant-cli.js +4 -0
- package/dist/kernel/ports/host-cli.js +3 -0
- package/dist/kernel/ports/interest.js +4 -0
- package/dist/kernel/ports/languages.js +0 -3
- package/dist/kernel/ports/model.js +2 -0
- package/dist/kernel/ports/settings-places.js +31 -0
- package/dist/kernel/ports/system-speech.js +31 -3
- package/dist/kernel/runner/attempt.js +14 -4
- package/dist/kernel/runner/audition.js +3 -1
- package/dist/kernel/runner/checkpoint-authority.js +2 -1
- package/dist/kernel/runner/index.js +8 -10
- package/dist/kernel/runner/model-access.js +40 -0
- package/dist/kernel/runner/model-check.js +153 -0
- package/dist/kernel/runner/providers.js +18 -12
- package/dist/kernel/runner/standalone.js +11 -7
- package/dist/main.js +338 -21
- package/dist/model-catalog.js +20 -0
- package/dist/model-checks.js +166 -0
- package/dist/patch-notes/4.0.0.md +51 -0
- package/dist/patch-notes/4.1.0.md +42 -0
- package/dist/patch-notes/index.json +12 -0
- package/dist/slices/admission/heads.js +2 -1
- package/dist/slices/admission/listing-facts.js +95 -0
- package/dist/slices/admission/repo.js +11 -7
- package/dist/slices/admission/rules.js +50 -15
- package/dist/slices/admission/schema.js +8 -0
- package/dist/slices/admission/stage-writes.js +0 -3
- package/dist/slices/admission/start.js +48 -31
- package/dist/slices/admission/state-words.js +34 -5
- package/dist/slices/admission/substitute.js +7 -4
- package/dist/slices/agents/attention-posts.js +31 -0
- package/dist/slices/agents/attention-templates.js +33 -0
- package/dist/slices/agents/attention.js +10 -1
- package/dist/slices/agents/connection.js +2 -6
- package/dist/slices/agents/model.js +6 -3
- package/dist/slices/agents/repo.js +15 -7
- package/dist/slices/article/continuation.js +0 -63
- package/dist/slices/article/plain.js +3 -2
- package/dist/slices/article/segments.js +2 -62
- package/dist/slices/assistant/access.js +1 -0
- package/dist/slices/assistant/asks.js +80 -0
- package/dist/slices/assistant/cost.js +54 -0
- package/dist/slices/assistant/gate.js +222 -0
- package/dist/slices/assistant/model.js +86 -0
- package/dist/slices/assistant/names.js +23 -0
- package/dist/slices/assistant/prompt.js +74 -0
- package/dist/slices/assistant/repo.js +74 -0
- package/dist/slices/assistant/tokens.js +18 -0
- package/dist/slices/assistant/turn.js +344 -0
- package/dist/slices/autopilot/buffer.js +76 -0
- package/dist/slices/autopilot/model.js +14 -0
- package/dist/slices/autopilot/notify.js +27 -0
- package/dist/slices/autopilot/repo.js +34 -0
- package/dist/slices/autopilot/signals.js +194 -0
- package/dist/slices/autopilot/warnings.js +85 -0
- package/dist/slices/autopilot/weekly.js +100 -0
- package/dist/slices/backups/files.js +2 -6
- package/dist/slices/backups/repo.js +3 -15
- package/dist/slices/backups/service.js +8 -7
- package/dist/slices/channels/cast-images.js +3 -2
- package/dist/slices/channels/model.js +0 -1
- package/dist/slices/channels/rebrand.js +0 -3
- package/dist/slices/channels/repo.js +4 -0
- package/dist/slices/channels/schema.js +7 -1
- package/dist/slices/channels/service.js +10 -0
- package/dist/slices/channels/videos.js +3 -15
- package/dist/slices/checkpoints/index.js +0 -1
- package/dist/slices/control/providers.js +2 -28
- package/dist/slices/designs/artwork.js +66 -0
- package/dist/slices/designs/budget.js +23 -0
- package/dist/slices/designs/calls.js +124 -0
- package/dist/slices/designs/check-context.js +38 -0
- package/dist/slices/designs/check-text.js +43 -0
- package/dist/slices/designs/fields.js +88 -0
- package/dist/slices/designs/frames.js +34 -0
- package/dist/slices/designs/gate.js +130 -0
- package/dist/slices/designs/layout.js +293 -0
- package/dist/slices/designs/model.js +35 -0
- package/dist/slices/designs/pdf-job.js +23 -0
- package/dist/slices/designs/pdf-pages.js +30 -0
- package/dist/slices/designs/pdf-runner.js +68 -0
- package/dist/slices/designs/pdf-worker.js +24 -0
- package/dist/slices/designs/picture-size.js +79 -0
- package/dist/slices/designs/pictures.js +55 -0
- package/dist/slices/designs/quote.js +24 -0
- package/dist/slices/designs/render-args.js +36 -0
- package/dist/slices/designs/render-input.js +59 -0
- package/dist/slices/designs/render.js +215 -0
- package/dist/slices/designs/repo.js +101 -0
- package/dist/slices/designs/requests.js +80 -0
- package/dist/slices/designs/runner.js +239 -0
- package/dist/slices/designs/sample-repo.js +91 -0
- package/dist/slices/designs/sample.js +223 -0
- package/dist/slices/designs/schema.js +87 -0
- package/dist/slices/designs/sections.js +44 -0
- package/dist/slices/designs/service.js +233 -0
- package/dist/slices/designs/steps-repo.js +102 -0
- package/dist/slices/designs/studio-kind.js +39 -0
- package/dist/slices/designs/template-checks.js +44 -0
- package/dist/slices/designs/template-make.js +118 -0
- package/dist/slices/designs/templates.js +218 -0
- package/dist/slices/document/fonts.js +2 -1
- package/dist/slices/document/model.js +7 -4
- package/dist/slices/document/render.js +2 -1
- package/dist/slices/document/theme-schema.js +1 -1
- package/dist/slices/document/theme.js +49 -3
- package/dist/slices/episodes/summarize.js +6 -2
- package/dist/slices/estimate/index.js +72 -28
- package/dist/slices/estimate/requests.js +4 -4
- package/dist/slices/fixes/rules.js +3 -1
- package/dist/slices/fonts/catalog.js +5 -3
- package/dist/slices/fonts/coverage.js +2 -1
- package/dist/slices/fonts/discovery.js +3 -2
- package/dist/slices/fonts/files.js +2 -6
- package/dist/slices/fonts/index.js +0 -1
- package/dist/slices/fonts/sfnt.js +45 -10
- package/dist/slices/images/appearance.js +8 -16
- package/dist/slices/images/scenes.js +102 -28
- package/dist/slices/keywords/digest.js +85 -0
- package/dist/slices/keywords/facts.js +92 -0
- package/dist/slices/keywords/fill.js +80 -0
- package/dist/slices/keywords/model.js +75 -0
- package/dist/slices/keywords/rules.js +187 -0
- package/dist/slices/keywords/schema.js +28 -0
- package/dist/slices/keywords/specs.js +260 -0
- package/dist/slices/keywords/values.js +74 -0
- package/dist/slices/library/lint.js +29 -12
- package/dist/slices/library/model.js +2 -0
- package/dist/slices/library/photorealistic.js +4 -12
- package/dist/slices/library/rename.js +9 -7
- package/dist/slices/library/save.js +14 -10
- package/dist/slices/library/slots.js +37 -11
- package/dist/slices/library/transfer.js +5 -4
- package/dist/slices/library/used-by.js +25 -10
- package/dist/slices/loudness/loudnorm.js +53 -12
- package/dist/slices/loudness/model.js +3 -2
- package/dist/slices/loudness/sentence-level.js +2 -1
- package/dist/slices/model-checks/cost.js +61 -0
- package/dist/slices/model-checks/listing.js +124 -0
- package/dist/slices/model-checks/model.js +20 -0
- package/dist/slices/model-checks/record.js +32 -0
- package/dist/slices/model-checks/repo.js +120 -0
- package/dist/slices/model-checks/rules.js +125 -0
- package/dist/slices/model-checks/run.js +143 -0
- package/dist/slices/model-checks/service.js +198 -0
- package/dist/slices/model-upkeep/model.js +1 -1
- package/dist/slices/model-upkeep/switch.js +72 -27
- package/dist/slices/model-upkeep/usage.js +39 -13
- package/dist/slices/narration/breaths.js +3 -1
- package/dist/slices/narration/clean-pieces.js +3 -1
- package/dist/slices/narration/concat.js +2 -1
- package/dist/slices/narration/pauses-model.js +3 -2
- package/dist/slices/narration/peaks.js +3 -4
- package/dist/slices/narration/pronunciation-chunks.js +4 -3
- package/dist/slices/narration/pronunciation.js +6 -4
- package/dist/slices/narration/steering.js +4 -3
- package/dist/slices/notifications/notifier.js +4 -3
- package/dist/slices/notifications/rules.js +5 -2
- package/dist/slices/notifications/settings.js +4 -12
- package/dist/slices/notifications/subject.js +4 -3
- package/dist/slices/onboarding/copy.js +21 -3
- package/dist/slices/onboarding/first-run.js +16 -40
- package/dist/slices/onboarding/full-video.js +3 -2
- package/dist/slices/onboarding/install.js +6 -4
- package/dist/slices/onboarding/model.js +26 -37
- package/dist/slices/onboarding/packs.js +22 -22
- package/dist/slices/onboarding/preferred-model.js +14 -0
- package/dist/slices/onboarding/quick-short.js +105 -65
- package/dist/slices/onboarding/sample.js +4 -2
- package/dist/slices/onboarding/state.js +9 -11
- package/dist/slices/onboarding/template.js +8 -6
- package/dist/slices/outputs/add.js +3 -2
- package/dist/slices/outputs/service.js +2 -2
- package/dist/slices/patch-notes/library.js +4 -3
- package/dist/slices/play-drafts/convert.js +4 -0
- package/dist/slices/play-drafts/readiness.js +3 -3
- package/dist/slices/play-drafts/review-inputs.js +9 -3
- package/dist/slices/play-drafts/review.js +5 -4
- package/dist/slices/play-drafts/schema.js +4 -0
- package/dist/slices/play-drafts/service.js +17 -7
- package/dist/slices/project-templates/from-project.js +9 -2
- package/dist/slices/project-templates/next-chapter.js +2 -1
- package/dist/slices/project-templates/provider-defaults.js +22 -0
- package/dist/slices/project-templates/repo.js +6 -1
- package/dist/slices/project-templates/setup-sections.js +26 -0
- package/dist/slices/project-templates/setup.js +14 -1
- package/dist/slices/project-templates/stand-ins.js +22 -0
- package/dist/slices/rebuild/activity.js +2 -0
- package/dist/slices/rebuild/admission-repo.js +3 -2
- package/dist/slices/rebuild/narration-history.js +4 -5
- package/dist/slices/rebuild/narration-retry.js +2 -1
- package/dist/slices/rebuild/preview-details.js +32 -14
- package/dist/slices/rebuild/preview-plan.js +3 -8
- package/dist/slices/rebuild/recipe-build.js +8 -3
- package/dist/slices/rebuild/recipe-describe.js +3 -3
- package/dist/slices/rebuild/recipe-digest.js +58 -0
- package/dist/slices/rebuild/recipe-input-schema.js +10 -0
- package/dist/slices/rebuild/recipe-keywords.js +71 -0
- package/dist/slices/rebuild/recipe-legacy.js +5 -1
- package/dist/slices/rebuild/recipe-model.js +4 -0
- package/dist/slices/rebuild/recipe-narration-text.js +2 -1
- package/dist/slices/rebuild/recipe-preparation.js +4 -4
- package/dist/slices/rebuild/recipe-reference.js +8 -4
- package/dist/slices/rebuild/recipe-reviews.js +27 -7
- package/dist/slices/rebuild/recipe-save.js +8 -5
- package/dist/slices/rebuild/recipe-scenes.js +40 -11
- package/dist/slices/rebuild/recipe-shorts.js +8 -8
- package/dist/slices/rebuild/recipe-social.js +46 -0
- package/dist/slices/rebuild/recipe-text.js +64 -20
- package/dist/slices/rebuild/recipe-validation.js +102 -31
- package/dist/slices/rebuild/recipe-visual.js +55 -16
- package/dist/slices/rebuild/recipe-voices.js +3 -2
- package/dist/slices/rebuild/recipe-work.js +19 -7
- package/dist/slices/rebuild/recipe-youtube.js +4 -3
- package/dist/slices/rebuild/recovery-selection.js +3 -1
- package/dist/slices/rebuild/recovery.js +9 -8
- package/dist/slices/rebuild/remake-outdated.js +5 -5
- package/dist/slices/rebuild/repo.js +4 -3
- package/dist/slices/rebuild/review-promise.js +25 -0
- package/dist/slices/rebuild/review-redo.js +3 -2
- package/dist/slices/rebuild/runtime-admission.js +4 -3
- package/dist/slices/rebuild/runtime-agent-review.js +11 -5
- package/dist/slices/rebuild/runtime-animate.js +10 -9
- package/dist/slices/rebuild/runtime-article.js +7 -5
- package/dist/slices/rebuild/runtime-checkpoints.js +5 -4
- package/dist/slices/rebuild/runtime-document.js +11 -13
- package/dist/slices/rebuild/runtime-export-bed.js +8 -12
- package/dist/slices/rebuild/runtime-export-edit.js +8 -16
- package/dist/slices/rebuild/runtime-export-inputs.js +12 -11
- package/dist/slices/rebuild/runtime-export-short.js +9 -5
- package/dist/slices/rebuild/runtime-export.js +38 -15
- package/dist/slices/rebuild/runtime-figure-card.js +2 -1
- package/dist/slices/rebuild/runtime-image.js +8 -12
- package/dist/slices/rebuild/runtime-instructions.js +2 -0
- package/dist/slices/rebuild/runtime-keywords.js +59 -0
- package/dist/slices/rebuild/runtime-lines.js +2 -11
- package/dist/slices/rebuild/runtime-local.js +17 -22
- package/dist/slices/rebuild/runtime-narration-publication.js +2 -5
- package/dist/slices/rebuild/runtime-narration-reuse.js +9 -13
- package/dist/slices/rebuild/runtime-narration-text.js +13 -17
- package/dist/slices/rebuild/runtime-pictures.js +2 -4
- package/dist/slices/rebuild/runtime-piece-label.js +10 -6
- package/dist/slices/rebuild/runtime-plan.js +31 -5
- package/dist/slices/rebuild/runtime-provider.js +67 -25
- package/dist/slices/rebuild/runtime-publication.js +10 -9
- package/dist/slices/rebuild/runtime-review.js +39 -33
- package/dist/slices/rebuild/runtime-run.js +4 -1
- package/dist/slices/rebuild/runtime-shorts.js +31 -50
- package/dist/slices/rebuild/runtime-store.js +20 -10
- package/dist/slices/rebuild/runtime-subtitles.js +28 -25
- package/dist/slices/rebuild/runtime-voices.js +10 -16
- package/dist/slices/rebuild/runtime-youtube.js +14 -31
- package/dist/slices/rebuild/service-readiness.js +11 -3
- package/dist/slices/rebuild/transition-repo.js +6 -5
- package/dist/slices/rebuild/work-records.js +6 -0
- package/dist/slices/reruns/cascade.js +1 -2
- package/dist/slices/reruns/index.js +6 -9
- package/dist/slices/research/synthesis.js +3 -2
- package/dist/slices/reviews/external-model.js +4 -1
- package/dist/slices/reviews/external-repo.js +2 -1
- package/dist/slices/reviews/external-rows.js +2 -3
- package/dist/slices/reviews/holds.js +5 -4
- package/dist/slices/reviews/model.js +32 -11
- package/dist/slices/reviews/repo.js +2 -1
- package/dist/slices/reviews/rules.js +4 -3
- package/dist/slices/reviews/verdict.js +2 -1
- package/dist/slices/reviews/view.js +5 -2
- package/dist/slices/revisions/downloads.js +4 -5
- package/dist/slices/revisions/image-plan.js +1 -1
- package/dist/slices/revisions/mutation-assets.js +9 -5
- package/dist/slices/revisions/mutation-cues.js +2 -5
- package/dist/slices/revisions/mutations.js +3 -3
- package/dist/slices/revisions/repo.js +3 -1
- package/dist/slices/revisions/schema.js +1 -0
- package/dist/slices/revisions/view.js +20 -0
- package/dist/slices/run-cost/limits.js +5 -2
- package/dist/slices/run-cost/meter.js +12 -5
- package/dist/slices/run-cost/panel.js +8 -74
- package/dist/slices/run-cost/plan-readings.js +127 -0
- package/dist/slices/run-cost/plan-use.js +169 -0
- package/dist/slices/run-cost/pricing.js +3 -3
- package/dist/slices/run-cost/week.js +32 -4
- package/dist/slices/schedules/agenda.js +18 -7
- package/dist/slices/schedules/by-date.js +42 -0
- package/dist/slices/schedules/calendar.js +5 -0
- package/dist/slices/schedules/design-agenda.js +36 -0
- package/dist/slices/schedules/design-check.js +41 -0
- package/dist/slices/schedules/design-run.js +126 -0
- package/dist/slices/schedules/next-run.js +62 -0
- package/dist/slices/schedules/prepare.js +18 -2
- package/dist/slices/schedules/repo.js +30 -21
- package/dist/slices/schedules/rescore.js +35 -0
- package/dist/slices/schedules/scheduler.js +13 -3
- package/dist/slices/schedules/schema.js +57 -3
- package/dist/slices/schedules/score.js +107 -0
- package/dist/slices/schedules/service.js +57 -24
- package/dist/slices/schedules/topic-list.js +15 -0
- package/dist/slices/schedules/topic-scores.js +104 -0
- package/dist/slices/schedules/topics.js +53 -41
- package/dist/slices/settings/cli-paths.js +4 -6
- package/dist/slices/settings/first-run.js +8 -43
- package/dist/slices/settings/health.js +30 -15
- package/dist/slices/settings/key-test.js +1 -1
- package/dist/slices/settings/model-reach.js +3 -17
- package/dist/slices/settings/model.js +14 -2
- package/dist/slices/settings/playback.js +34 -23
- package/dist/slices/settings/probe-read.js +16 -0
- package/dist/slices/settings/repo.js +33 -0
- package/dist/slices/settings/tutorial.js +2 -2
- package/dist/slices/settings/voices.js +4 -12
- package/dist/slices/shorts/keep.js +2 -1
- package/dist/slices/shorts/pick.js +6 -18
- package/dist/slices/shorts/prompts.js +4 -14
- package/dist/slices/shorts/render.js +6 -2
- package/dist/slices/social/channel.js +44 -0
- package/dist/slices/social/defaults.js +17 -0
- package/dist/slices/social/due.js +36 -0
- package/dist/slices/social/extension.js +18 -0
- package/dist/slices/social/fit.js +115 -0
- package/dist/slices/social/limits.js +186 -0
- package/dist/slices/social/links.js +21 -0
- package/dist/slices/social/media.js +132 -0
- package/dist/slices/social/model.js +75 -0
- package/dist/slices/social/notice.js +19 -0
- package/dist/slices/social/pack.js +176 -0
- package/dist/slices/social/post-prompt.js +56 -0
- package/dist/slices/social/queue.js +31 -0
- package/dist/slices/social/repo.js +133 -0
- package/dist/slices/social/reshape.js +97 -0
- package/dist/slices/social/rules.js +5 -0
- package/dist/slices/social/schema.js +70 -0
- package/dist/slices/social/serve.js +51 -0
- package/dist/slices/social/service.js +129 -0
- package/dist/slices/social/settings.js +21 -0
- package/dist/slices/social/sources.js +114 -0
- package/dist/slices/social/state.js +110 -0
- package/dist/slices/social/text.js +149 -0
- package/dist/slices/storage/arrange.js +3 -1
- package/dist/slices/storage/assets.js +8 -10
- package/dist/slices/storage/backup-designs.js +74 -0
- package/dist/slices/storage/backup-export.js +8 -3
- package/dist/slices/storage/backup-format.js +10 -0
- package/dist/slices/storage/backup-import.js +22 -4
- package/dist/slices/storage/delete-project.js +6 -5
- package/dist/slices/storage/files-location.js +6 -8
- package/dist/slices/storage/layout.js +13 -2
- package/dist/slices/storage/model.js +3 -0
- package/dist/slices/storage/portable.js +17 -12
- package/dist/slices/storage/schema.js +2 -0
- package/dist/slices/storage/staging-refs.js +2 -1
- package/dist/slices/storage/staging.js +6 -9
- package/dist/slices/storage/trim.js +26 -9
- package/dist/slices/studio/calendar.js +7 -2
- package/dist/slices/studio/disclosure.js +4 -4
- package/dist/slices/studio/model.js +1 -1
- package/dist/slices/studio/pack.js +9 -8
- package/dist/slices/studio/pick.js +2 -11
- package/dist/slices/studio/plan.js +7 -16
- package/dist/slices/studio/queue.js +3 -14
- package/dist/slices/studio/releases.js +26 -0
- package/dist/slices/studio/settings.js +20 -57
- package/dist/slices/studio/videos.js +35 -4
- package/dist/slices/style-preview/render.js +37 -16
- package/dist/slices/style-preview/schema.js +14 -1
- package/dist/slices/style-preview/service.js +2 -1
- package/dist/slices/style-preview/settings.js +33 -5
- package/dist/slices/subtitles/captions.js +1 -1
- package/dist/slices/telemetry/collector-client.js +2 -4
- package/dist/slices/telemetry/flush.js +2 -4
- package/dist/slices/telemetry/model.js +0 -7
- package/dist/slices/telemetry/record.js +10 -3
- package/dist/slices/template-studio/anatomy-view.js +161 -0
- package/dist/slices/template-studio/anatomy.js +73 -0
- package/dist/slices/template-studio/bakeoff-make.js +225 -0
- package/dist/slices/template-studio/bakeoff-repo.js +83 -0
- package/dist/slices/template-studio/bakeoff.js +333 -0
- package/dist/slices/template-studio/budget.js +106 -0
- package/dist/slices/template-studio/cards.js +78 -0
- package/dist/slices/template-studio/checks.js +322 -0
- package/dist/slices/template-studio/document-sample.js +56 -0
- package/dist/slices/template-studio/edit.js +15 -0
- package/dist/slices/template-studio/for-all.js +161 -0
- package/dist/slices/template-studio/free-video.js +31 -0
- package/dist/slices/template-studio/free.js +176 -0
- package/dist/slices/template-studio/gate.js +40 -0
- package/dist/slices/template-studio/hidden.js +20 -0
- package/dist/slices/template-studio/jobs.js +79 -0
- package/dist/slices/template-studio/loudness.js +111 -0
- package/dist/slices/template-studio/make.js +40 -0
- package/dist/slices/template-studio/model.js +58 -0
- package/dist/slices/template-studio/pack-import.js +253 -0
- package/dist/slices/template-studio/pack-samples.js +60 -0
- package/dist/slices/template-studio/pack.js +207 -0
- package/dist/slices/template-studio/paid-view.js +66 -0
- package/dist/slices/template-studio/paid.js +355 -0
- package/dist/slices/template-studio/quote.js +155 -0
- package/dist/slices/template-studio/ready.js +104 -0
- package/dist/slices/template-studio/repo.js +134 -0
- package/dist/slices/template-studio/reviews-path.js +58 -0
- package/dist/slices/template-studio/runner.js +84 -0
- package/dist/slices/template-studio/save.js +236 -0
- package/dist/slices/template-studio/say-it-as.js +248 -0
- package/dist/slices/template-studio/schedule-gate.js +34 -0
- package/dist/slices/template-studio/schedule-price.js +32 -0
- package/dist/slices/template-studio/schedule-sample.js +91 -0
- package/dist/slices/template-studio/schema.js +243 -0
- package/dist/slices/template-studio/scope.js +124 -0
- package/dist/slices/template-studio/sections.js +176 -0
- package/dist/slices/template-studio/service.js +175 -0
- package/dist/slices/template-studio/status.js +191 -0
- package/dist/slices/template-studio/topic.js +223 -0
- package/dist/slices/thumbnail/by-llm.js +1 -1
- package/dist/slices/trash/designs.js +76 -0
- package/dist/slices/trash/model.js +13 -4
- package/dist/slices/trash/service.js +52 -2
- package/dist/slices/tutorials/library.js +3 -2
- package/dist/slices/uploads/repo.js +37 -1
- package/dist/slices/uploads/rules.js +3 -2
- package/dist/slices/video/ass-text.js +17 -0
- package/dist/slices/video/audio-export.js +2 -64
- package/dist/slices/video/edit-list.js +2 -2
- package/dist/slices/video/edit-settings.js +1 -1
- package/dist/slices/video/encoding.js +169 -0
- package/dist/slices/video/ffmpeg.js +23 -21
- package/dist/slices/video/figure-card.js +1 -15
- package/dist/slices/video/plan.js +2 -1
- package/dist/slices/video/slideshow.js +43 -18
- package/dist/slices/voices/attribution.js +3 -2
- package/dist/slices/voices/model.js +9 -8
- package/dist/slices/voices/palette.js +0 -3
- package/dist/slices/voices/script.js +2 -1
- package/dist/slices/youtube/answer.js +4 -16
- package/dist/slices/youtube/edits-repo.js +3 -12
- package/dist/tutorials/AI-CLIs.md +25 -25
- package/dist/tutorials/Agents-MCP.md +34 -33
- package/dist/tutorials/Assistant.md +42 -0
- package/dist/tutorials/Backups.md +7 -7
- package/dist/tutorials/Calendar.md +17 -25
- package/dist/tutorials/Cast-Library.md +19 -19
- package/dist/tutorials/Channels.md +42 -35
- package/dist/tutorials/Costs-and-Run-Cost.md +38 -40
- package/dist/tutorials/Create-Article.md +114 -0
- package/dist/tutorials/Create-Extras.md +112 -0
- package/dist/tutorials/Create-Images.md +165 -0
- package/dist/tutorials/Create-Narration.md +178 -0
- package/dist/tutorials/Create-Overview.md +244 -0
- package/dist/tutorials/Create-Video-and-Style.md +144 -0
- package/dist/tutorials/Docker.md +3 -3
- package/dist/tutorials/Document-Themes.md +32 -32
- package/dist/tutorials/Editing-a-Project.md +94 -94
- package/dist/tutorials/FAQ.md +18 -18
- package/dist/tutorials/First-Launch-and-Welcome.md +50 -64
- package/dist/tutorials/Home-and-Projects.md +115 -71
- package/dist/tutorials/Home.md +33 -33
- package/dist/tutorials/Install.md +14 -13
- package/dist/tutorials/Intros-and-Outros.md +33 -34
- package/dist/tutorials/Keyboard-Shortcuts-and-Command-Palette.md +62 -58
- package/dist/tutorials/Keywords-Slopify-Fills.md +87 -0
- package/dist/tutorials/Models.md +53 -21
- package/dist/tutorials/Multiple-Voices.md +41 -40
- package/dist/tutorials/Narration-Aliases-and-Glossary.md +14 -14
- package/dist/tutorials/Notifications.md +27 -17
- package/dist/tutorials/Other-Languages.md +15 -15
- package/dist/tutorials/PDF-Documents.md +38 -29
- package/dist/tutorials/Posting-to-Social-Sites.md +121 -0
- package/dist/tutorials/Project-Page.md +118 -120
- package/dist/tutorials/Prompts-and-Templates.md +84 -0
- package/dist/tutorials/Prompts.md +48 -44
- package/dist/tutorials/Providers-and-Keys.md +105 -98
- package/dist/tutorials/Publishing-to-YouTube.md +31 -31
- package/dist/tutorials/Recovery-and-Retries.md +31 -31
- package/dist/tutorials/Remote-Access.md +63 -0
- package/dist/tutorials/Reviews-and-Checkpoints.md +98 -90
- package/dist/tutorials/Schedules.md +46 -27
- package/dist/tutorials/Settings-Reference.md +194 -174
- package/dist/tutorials/Shorts.md +34 -33
- package/dist/tutorials/Start-at-Login.md +3 -3
- package/dist/tutorials/Studio-Extension.md +22 -24
- package/dist/tutorials/Template-Studio.md +128 -0
- package/dist/tutorials/Templates.md +51 -77
- package/dist/tutorials/Trash.md +4 -4
- package/dist/tutorials/Troubleshooting.md +44 -44
- package/dist/tutorials/Uninstalling-and-Moving.md +5 -5
- package/dist/tutorials/Updating-and-Patch-Notes.md +18 -18
- package/dist/tutorials/Video-Editing.md +34 -34
- package/dist/tutorials/Where-Your-Files-Live.md +10 -10
- package/dist/tutorials/YouTube-Description.md +26 -26
- package/dist/tutorials/Your-First-Short.md +35 -35
- package/dist/tutorials/_Sidebar.md +19 -14
- package/dist/updater/work-in-progress.js +42 -12
- package/dist/web/assets/index-BNftKQrA.css +1 -0
- package/dist/web/assets/index-Cpa58woP.js +459 -0
- package/dist/web/assets/pdf-BKh8f7R6.js +56 -0
- package/dist/web/assets/pdf.worker.min-BmVo14Nb.mjs +28 -0
- package/dist/web/assets/pdf.worker.min-D7i8o3wR.js +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +6 -2
- package/dist/slices/article/provided-entries.js +0 -66
- package/dist/slices/article/run.js +0 -145
- package/dist/slices/images/run.js +0 -234
- package/dist/slices/narration/legacy-plan.js +0 -58
- package/dist/slices/narration/run.js +0 -270
- package/dist/slices/rebuild/runtime-actions.js +0 -90
- package/dist/slices/research/run.js +0 -224
- package/dist/slices/subtitles/prepare.js +0 -194
- package/dist/slices/subtitles/transcript.js +0 -34
- package/dist/slices/thumbnail/run.js +0 -219
- package/dist/slices/video/audio-inputs.js +0 -33
- package/dist/slices/video/reuse-audio.js +0 -62
- package/dist/slices/video/run.js +0 -78
- package/dist/slices/video/write-export.js +0 -158
- package/dist/slices/video/write-subtitles.js +0 -59
- package/dist/tutorials/Library-Overview.md +0 -76
- package/dist/tutorials/Play-Images.md +0 -161
- package/dist/tutorials/Play-Narration.md +0 -167
- package/dist/tutorials/Play-Outputs.md +0 -109
- package/dist/tutorials/Play-Overview.md +0 -227
- package/dist/tutorials/Play-Title-and-Article.md +0 -136
- package/dist/tutorials/Play-Video-and-Style.md +0 -143
- package/dist/web/assets/index-BadsoORk.css +0 -1
- package/dist/web/assets/index-D8LqNsCl.js +0 -326
- package/dist/web/assets/pdf-T7MCwjIv.js +0 -54
- package/dist/web/assets/pdf.worker.min-Dswkl-cV.mjs +0 -29
- package/dist/web/assets/pdf.worker.min-zC6nfbxG.js +0 -1
|
@@ -1,31 +1,5 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
1
|
import { checkRuntimeModel } from "../../catalog/runtime-models.js";
|
|
3
|
-
import { thinkingModes } from "../../kernel/ports/llm.js";
|
|
4
2
|
import { readinessIsUsable, } from "../../kernel/ports/model.js";
|
|
5
|
-
import { chunkModes } from "../narration/chunk.js";
|
|
6
|
-
const choice = z
|
|
7
|
-
.object({
|
|
8
|
-
provider: z.string().trim().min(1).max(200),
|
|
9
|
-
model: z.string().trim().min(1).max(200),
|
|
10
|
-
thinking: z.enum(thinkingModes).optional(),
|
|
11
|
-
})
|
|
12
|
-
.strict();
|
|
13
|
-
export const providerChangesSchema = z
|
|
14
|
-
.object({
|
|
15
|
-
llm: choice.optional(),
|
|
16
|
-
audio: choice.extend({ voice: z.string().trim().min(1).max(200) }).optional(),
|
|
17
|
-
images: choice.optional(),
|
|
18
|
-
chunking: z
|
|
19
|
-
.object({
|
|
20
|
-
mode: z.enum(chunkModes),
|
|
21
|
-
words: z.number().int().min(1).max(10000).optional(),
|
|
22
|
-
characters: z.number().int().min(1).max(1000000).optional(),
|
|
23
|
-
})
|
|
24
|
-
.strict()
|
|
25
|
-
.optional(),
|
|
26
|
-
})
|
|
27
|
-
.strict()
|
|
28
|
-
.refine((value) => Object.keys(value).length > 0, "Change at least one provider before saving.");
|
|
29
3
|
export function validateLocalProviderChanges(changes, current) {
|
|
30
4
|
const fields = [];
|
|
31
5
|
const families = { llm: "llm", audio: "tts", images: "image" };
|
|
@@ -50,8 +24,8 @@ export function validateLocalProviderChanges(changes, current) {
|
|
|
50
24
|
"Install this command-line tool on your computer and sign in to it, then try again.")
|
|
51
25
|
: provider.readiness.kind === "local"
|
|
52
26
|
? (provider.readiness.issue ??
|
|
53
|
-
"No speech program was found on this computer. Install
|
|
54
|
-
: "Add this provider's API key in Settings →
|
|
27
|
+
"No speech program was found on this computer. Install the free program eSpeak NG, or choose a voice service with a key in Settings → Keys & services.")
|
|
28
|
+
: "Add this provider's API key in Settings → Keys & services first.",
|
|
55
29
|
});
|
|
56
30
|
}
|
|
57
31
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { castMemberById, imageBlob } from "../channels/repo.js";
|
|
2
|
+
import { designFields, fieldProblems, fillDocument, missingSentence, missingValues, } from "./fields.js";
|
|
3
|
+
import { artworkAspect } from "./frames.js";
|
|
4
|
+
import { designDocumentSchema } from "./schema.js";
|
|
5
|
+
export function artworkRequest(document, values) {
|
|
6
|
+
const parsed = designDocumentSchema.safeParse(document);
|
|
7
|
+
if (!parsed.success)
|
|
8
|
+
return {
|
|
9
|
+
ok: false,
|
|
10
|
+
reason: "invalid",
|
|
11
|
+
message: "This design has no settings to make artwork from. Choose its picture, text and formats first.",
|
|
12
|
+
};
|
|
13
|
+
const doc = parsed.data;
|
|
14
|
+
if (doc.picture.source !== "generate")
|
|
15
|
+
return {
|
|
16
|
+
ok: false,
|
|
17
|
+
reason: "invalid",
|
|
18
|
+
message: "This design uses its own picture as the artwork, so there is nothing to make. Edit the picture instead, or switch Picture to Made by the AI.",
|
|
19
|
+
};
|
|
20
|
+
const problem = fieldProblems(doc)[0];
|
|
21
|
+
if (problem !== undefined)
|
|
22
|
+
return { ok: false, reason: "invalid", message: problem.sentence };
|
|
23
|
+
const missing = missingValues(doc, values).filter((name) => detectedInPicture(doc, name));
|
|
24
|
+
if (missing.length > 0)
|
|
25
|
+
return { ok: false, reason: "missing-values", message: missingSentence(missing) };
|
|
26
|
+
const filled = fillDocument(doc, values);
|
|
27
|
+
const words = filled.text.blocks.map((block) => block.text.trim()).filter((text) => text !== "");
|
|
28
|
+
const prompt = doc.picture.aiText && words.length > 0
|
|
29
|
+
? `${filled.picture.prompt}\n\nDraw this text into the picture as part of the design, spelled exactly as written, each line on its own:\n${words.map((line) => `"${line}"`).join("\n")}`
|
|
30
|
+
: `${filled.picture.prompt}\n\nLeave room for a title and details to be set over the picture; draw no text, letters or logos.`;
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
value: {
|
|
34
|
+
prompt,
|
|
35
|
+
aspect: artworkAspect(doc.formats.map((one) => one.format)),
|
|
36
|
+
castMemberIds: doc.picture.castMemberIds,
|
|
37
|
+
provider: doc.picture.provider,
|
|
38
|
+
model: doc.picture.model,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// The text fields only matter to the artwork when the AI draws the text.
|
|
43
|
+
function detectedInPicture(doc, name) {
|
|
44
|
+
if (doc.picture.aiText)
|
|
45
|
+
return designFields({ ...doc, brochure: undefined }).includes(name);
|
|
46
|
+
return designFields({ ...doc, text: { ...doc.text, blocks: [] }, brochure: undefined }).includes(name);
|
|
47
|
+
}
|
|
48
|
+
// Each chosen cast member with the pictures they have, in the order chosen.
|
|
49
|
+
export function castReferences(db, ids) {
|
|
50
|
+
const references = [];
|
|
51
|
+
for (const id of ids) {
|
|
52
|
+
const member = castMemberById(db, id);
|
|
53
|
+
if (member === undefined)
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
reason: "invalid",
|
|
57
|
+
message: "A cast member you chose was deleted. Choose the cast again, then press the button again.",
|
|
58
|
+
};
|
|
59
|
+
const images = member.images.flatMap((image) => {
|
|
60
|
+
const blob = image.state === "ready" && image.sha256 !== null ? imageBlob(db, image.sha256) : undefined;
|
|
61
|
+
return blob === undefined ? [] : [blob];
|
|
62
|
+
});
|
|
63
|
+
references.push({ name: member.name, description: member.description, images });
|
|
64
|
+
}
|
|
65
|
+
return { ok: true, value: references };
|
|
66
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readJsonSetting } from "../settings/repo.js";
|
|
3
|
+
import { sampleSpentToday } from "./sample-repo.js";
|
|
4
|
+
// What a design template's samples may spend per day, in dollars, before the editor holds a
|
|
5
|
+
// sample for a person to start.
|
|
6
|
+
// No row is seeded: an absent row reads as the default, and only a changed budget travels in
|
|
7
|
+
// backups.
|
|
8
|
+
export const dailySampleBudgetKey = "designs.dailySampleBudget";
|
|
9
|
+
export const dailySampleBudgetSchema = z.number().min(0).max(1000);
|
|
10
|
+
export const defaultDailySampleBudget = 1;
|
|
11
|
+
export function dailySampleBudget(db, log) {
|
|
12
|
+
return readJsonSetting(db, dailySampleBudgetKey, dailySampleBudgetSchema, defaultDailySampleBudget, log);
|
|
13
|
+
}
|
|
14
|
+
export function budgetSentence(total, budget) {
|
|
15
|
+
return `Waiting: this sample would take today's sample spending to $${total.toFixed(2)}, over the $${budget.toFixed(2)} a day samples may spend. Press Make sample to make it anyway; the limit starts again at midnight.`;
|
|
16
|
+
}
|
|
17
|
+
// Settings → Production defaults shows it beside the budget: what design samples spent since local
|
|
18
|
+
// midnight and what is left of today's budget.
|
|
19
|
+
export function designBudgetRoom(db, now, timeZone) {
|
|
20
|
+
const budget = dailySampleBudget(db);
|
|
21
|
+
const spent = sampleSpentToday(db, now, timeZone);
|
|
22
|
+
return { budget, spent, left: Math.max(0, budget - spent) };
|
|
23
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { transact } from "../../kernel/db/tx.js";
|
|
2
|
+
import { errorText } from "../../kernel/errors.js";
|
|
3
|
+
import { isProviderError } from "../../kernel/ports/model.js";
|
|
4
|
+
import { castImageMaxBytes, storeImageBlob } from "../channels/images.js";
|
|
5
|
+
import { imageBlob } from "../channels/repo.js";
|
|
6
|
+
import { backupMaxImageBytes } from "../storage/backup-format.js";
|
|
7
|
+
import { castReferences } from "./artwork.js";
|
|
8
|
+
import { pictureSize } from "./picture-size.js";
|
|
9
|
+
import { updateDesign } from "./repo.js";
|
|
10
|
+
import { settleStep, stepById } from "./steps-repo.js";
|
|
11
|
+
// The background half of the runner: each "making" step's provider call, at most two at once,
|
|
12
|
+
// settled into its step (and made the design's current version when it lands).
|
|
13
|
+
export const maxConcurrentDesignCalls = 2;
|
|
14
|
+
export const noProviderMessage = "No image service is set up. Add one in Settings → Keys & services, then press the button again.";
|
|
15
|
+
export function createDesignCalls(deps) {
|
|
16
|
+
let running = 0;
|
|
17
|
+
const waiting = [];
|
|
18
|
+
const inFlight = new Set();
|
|
19
|
+
const slot = async () => {
|
|
20
|
+
if (running < maxConcurrentDesignCalls) {
|
|
21
|
+
running += 1;
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
await new Promise((resolve) => waiting.push(resolve));
|
|
25
|
+
};
|
|
26
|
+
const release = () => {
|
|
27
|
+
const next = waiting.shift();
|
|
28
|
+
if (next === undefined)
|
|
29
|
+
running -= 1;
|
|
30
|
+
else
|
|
31
|
+
next();
|
|
32
|
+
};
|
|
33
|
+
const finish = (designId, stepId, outcome) => {
|
|
34
|
+
const at = deps.clock.now().toISOString();
|
|
35
|
+
transact(deps.db, () => {
|
|
36
|
+
if (settleStep(deps.db, stepId, outcome, at) && outcome.state === "ready")
|
|
37
|
+
updateDesign(deps.db, designId, undefined, { headStepId: stepId }, at);
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
deps.emit?.({ type: "design.changed", designId });
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
deps.log.write("warn", "designs.emit", { detail: errorText(error) });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const call = async (designId, stepId) => {
|
|
47
|
+
await slot();
|
|
48
|
+
try {
|
|
49
|
+
const step = stepById(deps.db, stepId);
|
|
50
|
+
const generate = deps.generate;
|
|
51
|
+
if (step === undefined ||
|
|
52
|
+
step.state !== "making" ||
|
|
53
|
+
step.provider === null ||
|
|
54
|
+
step.model === null)
|
|
55
|
+
return;
|
|
56
|
+
if (generate === undefined)
|
|
57
|
+
throw new Error(noProviderMessage);
|
|
58
|
+
const cast = castReferences(deps.db, step.inputs.castMemberIds);
|
|
59
|
+
if (!cast.ok)
|
|
60
|
+
throw new Error(cast.message);
|
|
61
|
+
const parent = step.parentId === null ? undefined : stepById(deps.db, step.parentId);
|
|
62
|
+
const source = parent === undefined || parent.sha256 === null
|
|
63
|
+
? undefined
|
|
64
|
+
: imageBlob(deps.db, parent.sha256);
|
|
65
|
+
if (step.kind === "edit" && source === undefined)
|
|
66
|
+
throw new Error("The version this edit starts from is no longer kept. Continue from another version, then press Edit.");
|
|
67
|
+
const sent = source === undefined || source.bytes.byteLength <= castImageMaxBytes
|
|
68
|
+
? source
|
|
69
|
+
: await shrunk(source.bytes);
|
|
70
|
+
const image = await generate({
|
|
71
|
+
designId,
|
|
72
|
+
purpose: step.kind === "edit" ? "picture-edit" : "design-art",
|
|
73
|
+
provider: step.provider,
|
|
74
|
+
model: step.model,
|
|
75
|
+
prompt: step.inputs.prompt ?? step.instruction ?? "",
|
|
76
|
+
aspect: step.inputs.aspect ?? "1:1",
|
|
77
|
+
...(sent === undefined || step.kind !== "edit" ? {} : { edit: { source: sent } }),
|
|
78
|
+
...(cast.value.length === 0 ? {} : { cast: cast.value }),
|
|
79
|
+
});
|
|
80
|
+
const size = pictureSize(image.bytes);
|
|
81
|
+
if (size === undefined || image.bytes.byteLength > backupMaxImageBytes)
|
|
82
|
+
throw new Error("The provider answered with something that is not a usable PNG or JPEG picture.");
|
|
83
|
+
const sha256 = storeImageBlob(deps.db, image.bytes, image.mime, deps.clock.now().toISOString());
|
|
84
|
+
finish(designId, stepId, { state: "ready", sha256, ...size });
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
const reason = errorText(error).slice(0, 500);
|
|
88
|
+
deps.log.write("warn", "designs.picture.failed", {
|
|
89
|
+
detail: `${designId}/${stepId}: ${reason}`,
|
|
90
|
+
});
|
|
91
|
+
finish(designId, stepId, { state: "failed", error: failedSentence(error, reason) });
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
release();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const shrunk = async (bytes) => {
|
|
98
|
+
if (deps.shrink === undefined)
|
|
99
|
+
throw new Error("The picture is too large to send to the provider.");
|
|
100
|
+
return { bytes: await deps.shrink(bytes), mime: "image/jpeg" };
|
|
101
|
+
};
|
|
102
|
+
const start = (designId, stepId) => {
|
|
103
|
+
const job = call(designId, stepId);
|
|
104
|
+
inFlight.add(job);
|
|
105
|
+
void job.finally(() => inFlight.delete(job));
|
|
106
|
+
};
|
|
107
|
+
return {
|
|
108
|
+
start,
|
|
109
|
+
idle: async () => {
|
|
110
|
+
while (inFlight.size > 0)
|
|
111
|
+
await Promise.allSettled([...inFlight]);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
// Try again is offered only when asking again can help: a model that can't do this, or a key
|
|
116
|
+
// the provider refuses, fails the same way every time, so the sentence says the fix instead.
|
|
117
|
+
function failedSentence(error, reason) {
|
|
118
|
+
const kind = isProviderError(error) ? error.fault.kind : undefined;
|
|
119
|
+
if (kind === "unsupported")
|
|
120
|
+
return `The picture couldn't be made: ${reason}${reason.includes("Made with") ? "" : " Choose another image model under Made with … · change."}`;
|
|
121
|
+
if (kind === "auth" || kind === "missing_key")
|
|
122
|
+
return `The picture couldn't be made: ${reason}${/Settings/.test(reason) ? "" : " Check the provider's key in Settings → Keys & services, then press Try again."}`;
|
|
123
|
+
return `The picture couldn't be made: ${reason} Press Try again, or choose another image model under Made with … · change.`;
|
|
124
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { errorText } from "../../kernel/errors.js";
|
|
2
|
+
import { readFontFile } from "../fonts/files.js";
|
|
3
|
+
// The fonts and providers Mark ready looks at, read from this computer. `main.ts` passes the
|
|
4
|
+
// real font catalogue and provider statuses; tests pass their own.
|
|
5
|
+
export function designCheckContext(deps) {
|
|
6
|
+
return {
|
|
7
|
+
font: async (fontId) => {
|
|
8
|
+
try {
|
|
9
|
+
const font = await deps.resolveFont(fontId);
|
|
10
|
+
const bytes = await readFontFile(font.path);
|
|
11
|
+
return bytes === undefined
|
|
12
|
+
? undefined
|
|
13
|
+
: { name: font.name, bytes: new Uint8Array(bytes), faceIndex: font.faceIndex };
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
providerProblem: async (provider) => {
|
|
20
|
+
let statuses;
|
|
21
|
+
try {
|
|
22
|
+
statuses = await deps.providers();
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return `Slopify couldn't check the image service: ${errorText(error)} Open Settings → Keys & services, then press Approve for schedules again.`;
|
|
26
|
+
}
|
|
27
|
+
const status = statuses.find((one) => one.id === provider);
|
|
28
|
+
if (status === undefined)
|
|
29
|
+
return `The image service “${provider}” isn't one this Slopify knows. Choose another image model under Picture.`;
|
|
30
|
+
const readiness = status.readiness;
|
|
31
|
+
if (readiness.kind === "keyed" && !readiness.hasKey)
|
|
32
|
+
return `${status.displayName} has no API key, so the picture can't be made. Add the key in Settings → Keys & services, then press Approve for schedules again.`;
|
|
33
|
+
if (readiness.kind === "cli" && (!readiness.installed || readiness.issue !== undefined))
|
|
34
|
+
return `${status.displayName} can't be used yet: ${readiness.issue ?? "it isn't installed on this computer."} Fix it in Settings → Keys & services, then press Approve for schedules again.`;
|
|
35
|
+
return null;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { render } from "../admission/substitute.js";
|
|
2
|
+
import { mappedCodepoints } from "../fonts/sfnt.js";
|
|
3
|
+
const squash = (value) => value.replace(/\s+/g, "");
|
|
4
|
+
export function checkText(input) {
|
|
5
|
+
const problems = [];
|
|
6
|
+
for (const [index, block] of input.text.blocks.entries()) {
|
|
7
|
+
const filled = render(block.text, input.values);
|
|
8
|
+
if (filled.trim() === "")
|
|
9
|
+
continue;
|
|
10
|
+
const where = `text block ${String(index + 1)}`;
|
|
11
|
+
const font = input.fonts[block.fontId];
|
|
12
|
+
if (font === undefined) {
|
|
13
|
+
problems.push({
|
|
14
|
+
blockId: block.id,
|
|
15
|
+
sentence: `The font chosen for ${where} is no longer on this computer. Choose another font for it under Text.`,
|
|
16
|
+
});
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const letters = [...new Set([...filled].filter((ch) => ch.trim() !== ""))];
|
|
20
|
+
const found = mappedCodepoints(font.bytes, font.faceIndex, letters.map((ch) => ch.codePointAt(0) ?? 0));
|
|
21
|
+
const missing = found === undefined ? [] : letters.filter((ch) => !found.has(ch.codePointAt(0) ?? 0));
|
|
22
|
+
if (missing.length > 0)
|
|
23
|
+
problems.push({
|
|
24
|
+
blockId: block.id,
|
|
25
|
+
sentence: `The font ${font.name} has no ${missing.map((ch) => `“${ch}”`).join(" ")} for ${where}. Choose another font for it under Text, or upload one that has ${missing.length === 1 ? "it" : "them"}.`,
|
|
26
|
+
});
|
|
27
|
+
const drawn = input.layout.lines
|
|
28
|
+
.filter((line) => line.blockId === block.id)
|
|
29
|
+
.map((line) => line.text)
|
|
30
|
+
.join("");
|
|
31
|
+
if (squash(drawn) !== squash(filled))
|
|
32
|
+
problems.push({
|
|
33
|
+
blockId: block.id,
|
|
34
|
+
sentence: `Slopify couldn't place ${where} exactly as typed on ${input.formatLabel}. Shorten it under Text, then press Make again.`,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (input.layout.overflow)
|
|
38
|
+
problems.push({
|
|
39
|
+
blockId: "",
|
|
40
|
+
sentence: `The text doesn't fit on ${input.formatLabel}, even at the smallest readable size. Shorten it or choose a placement with more room under Text.`,
|
|
41
|
+
});
|
|
42
|
+
return problems;
|
|
43
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { detectSlots, filledKeywords, render } from "../admission/substitute.js";
|
|
2
|
+
import { isReserved, reservedPrefix } from "../keywords/model.js";
|
|
3
|
+
function bodies(doc) {
|
|
4
|
+
return [
|
|
5
|
+
{ where: "the picture prompt", text: doc.picture.prompt },
|
|
6
|
+
...doc.text.blocks.map((block, index) => ({
|
|
7
|
+
where: `text block ${String(index + 1)}`,
|
|
8
|
+
text: block.text,
|
|
9
|
+
})),
|
|
10
|
+
...(doc.brochure === undefined
|
|
11
|
+
? []
|
|
12
|
+
: [
|
|
13
|
+
{ where: "the brochure title", text: doc.brochure.title },
|
|
14
|
+
{ where: "the brochure text", text: doc.brochure.body },
|
|
15
|
+
]),
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
export function designFields(doc) {
|
|
19
|
+
const names = [];
|
|
20
|
+
for (const body of bodies(doc))
|
|
21
|
+
for (const name of detectSlots(body.text).names)
|
|
22
|
+
if (!names.includes(name))
|
|
23
|
+
names.push(name);
|
|
24
|
+
return names;
|
|
25
|
+
}
|
|
26
|
+
const slotSentences = {
|
|
27
|
+
unclosed: "has a {{ without its }}",
|
|
28
|
+
empty: "has an empty {{}}",
|
|
29
|
+
nested: "has a { or } inside a {{keyword}}",
|
|
30
|
+
};
|
|
31
|
+
export function fieldProblems(doc) {
|
|
32
|
+
const problems = [];
|
|
33
|
+
for (const body of bodies(doc))
|
|
34
|
+
for (const error of detectSlots(body.text).errors)
|
|
35
|
+
problems.push({
|
|
36
|
+
kind: "slot",
|
|
37
|
+
where: body.where,
|
|
38
|
+
error: error.kind,
|
|
39
|
+
sentence: `The ${body.where.replace(/^the /, "")} ${slotSentences[error.kind]}. Fix it so every keyword reads {{Name}}.`,
|
|
40
|
+
});
|
|
41
|
+
const names = designFields(doc);
|
|
42
|
+
for (const name of names)
|
|
43
|
+
if (isReserved(name) || filledKeywords.includes(name))
|
|
44
|
+
problems.push({
|
|
45
|
+
kind: "reserved",
|
|
46
|
+
name,
|
|
47
|
+
sentence: `{{${name}}} is a name Slopify keeps for videos, so a design can't use it. Rename it (for example {{${isReserved(name) ? name.slice(reservedPrefix.length) : name}Text}}).`,
|
|
48
|
+
});
|
|
49
|
+
const dateField = doc.schedule?.dateField;
|
|
50
|
+
if (dateField !== undefined && !names.includes(dateField))
|
|
51
|
+
problems.push({
|
|
52
|
+
kind: "date-field",
|
|
53
|
+
name: dateField,
|
|
54
|
+
sentence: `The schedule reads each row's date from {{${dateField}}}, but no text uses it. Put {{${dateField}}} in a text block, or choose another date keyword under Schedule.`,
|
|
55
|
+
});
|
|
56
|
+
return problems;
|
|
57
|
+
}
|
|
58
|
+
// A field counts as filled only with something other than spaces.
|
|
59
|
+
export function missingValues(doc, values) {
|
|
60
|
+
return designFields(doc).filter((name) => (Object.hasOwn(values, name) ? (values[name] ?? "") : "").trim() === "");
|
|
61
|
+
}
|
|
62
|
+
export function missingSentence(missing) {
|
|
63
|
+
const list = missing.map((name) => `{{${name}}}`);
|
|
64
|
+
const named = list.length === 1
|
|
65
|
+
? `${list[0] ?? ""} has`
|
|
66
|
+
: `${list.slice(0, -1).join(", ")} and ${list.at(-1) ?? ""} have`;
|
|
67
|
+
return `${named} no value. Fill ${list.length === 1 ? "it" : "them"} in under Keywords, then press Make.`;
|
|
68
|
+
}
|
|
69
|
+
// Every text with its fields filled. Values are inserted verbatim, never read as fields.
|
|
70
|
+
export function fillDocument(doc, values) {
|
|
71
|
+
return {
|
|
72
|
+
...doc,
|
|
73
|
+
picture: { ...doc.picture, prompt: render(doc.picture.prompt, values) },
|
|
74
|
+
text: {
|
|
75
|
+
...doc.text,
|
|
76
|
+
blocks: doc.text.blocks.map((block) => ({ ...block, text: render(block.text, values) })),
|
|
77
|
+
},
|
|
78
|
+
...(doc.brochure === undefined
|
|
79
|
+
? {}
|
|
80
|
+
: {
|
|
81
|
+
brochure: {
|
|
82
|
+
...doc.brochure,
|
|
83
|
+
title: render(doc.brochure.title, values),
|
|
84
|
+
body: render(doc.brochure.body, values),
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { nearestFormat } from "./picture-size.js";
|
|
2
|
+
export const frames = {
|
|
3
|
+
a4: { width: 2480, height: 3508, pdf: { widthMm: 210, heightMm: 297 } },
|
|
4
|
+
a3: { width: 3508, height: 4961, pdf: { widthMm: 297, heightMm: 420 } },
|
|
5
|
+
flyer: { width: 1748, height: 2480, pdf: { widthMm: 148, heightMm: 210 } },
|
|
6
|
+
square: { width: 1080, height: 1080 },
|
|
7
|
+
portrait: { width: 1080, height: 1350 },
|
|
8
|
+
story: { width: 1080, height: 1920 },
|
|
9
|
+
wide: { width: 1920, height: 1080 },
|
|
10
|
+
// The brochure's cover is an A4 page; its inner pages are the document theme's own.
|
|
11
|
+
brochure: { width: 2480, height: 3508, pdf: { widthMm: 210, heightMm: 297 } },
|
|
12
|
+
};
|
|
13
|
+
// One artwork serves every format: the provider shape every chosen format is closest to, or
|
|
14
|
+
// square when they pull different ways, so each crop loses as little as it can.
|
|
15
|
+
export function artworkAspect(formats) {
|
|
16
|
+
const shapes = new Set(formats.map((format) => nearestFormat(frames[format])));
|
|
17
|
+
const [only] = shapes;
|
|
18
|
+
return shapes.size === 1 && only !== undefined ? only : "1:1";
|
|
19
|
+
}
|
|
20
|
+
// Cover-fit: the picture fills the frame, and what is cut off is cut around the focus point
|
|
21
|
+
// (0..1 on each axis of the picture), never past the picture's edge.
|
|
22
|
+
export function coverCrop(picture, frame, focus) {
|
|
23
|
+
const scale = Math.max(frame.width / picture.width, frame.height / picture.height);
|
|
24
|
+
const scaleWidth = Math.max(frame.width, Math.round(picture.width * scale));
|
|
25
|
+
const scaleHeight = Math.max(frame.height, Math.round(picture.height * scale));
|
|
26
|
+
// The frame is centred on the focus point, then slid back inside the picture.
|
|
27
|
+
const place = (size, window, at) => Math.min(size - window, Math.max(0, Math.round(at * size - window / 2)));
|
|
28
|
+
return {
|
|
29
|
+
scaleWidth,
|
|
30
|
+
scaleHeight,
|
|
31
|
+
x: place(scaleWidth, frame.width, focus.x),
|
|
32
|
+
y: place(scaleHeight, frame.height, focus.y),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { render } from "../admission/substitute.js";
|
|
3
|
+
import { fontAdvances, mappedCodepoints } from "../fonts/sfnt.js";
|
|
4
|
+
import { designFields } from "./fields.js";
|
|
5
|
+
import { frames } from "./frames.js";
|
|
6
|
+
import { layoutText } from "./layout.js";
|
|
7
|
+
import { formatLabels } from "./model.js";
|
|
8
|
+
import { sampleRow } from "./sample-repo.js";
|
|
9
|
+
import { templateRevision } from "./templates.js";
|
|
10
|
+
export function runnableDesignVersion(db, templateId) {
|
|
11
|
+
const row = db
|
|
12
|
+
.prepare(`SELECT r.version FROM design_template_revisions AS r
|
|
13
|
+
JOIN design_templates AS t ON t.id = r.template_id
|
|
14
|
+
WHERE r.template_id=? AND r.gate='ready' AND t.deleted_at IS NULL
|
|
15
|
+
ORDER BY r.version DESC LIMIT 1`)
|
|
16
|
+
.get(templateId);
|
|
17
|
+
if (row === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
const version = z.object({ version: z.number() }).parse(row).version;
|
|
20
|
+
const revision = templateRevision(db, templateId, version);
|
|
21
|
+
return revision === undefined
|
|
22
|
+
? undefined
|
|
23
|
+
: { version, name: revision.name, document: revision.document };
|
|
24
|
+
}
|
|
25
|
+
export async function contextChecks(doc, templateId, ctx) {
|
|
26
|
+
const at = (section, label = "Open") => ({
|
|
27
|
+
label,
|
|
28
|
+
href: `/library/design-templates/${templateId}#${section}`,
|
|
29
|
+
});
|
|
30
|
+
const checks = [];
|
|
31
|
+
if (doc.picture.source === "generate") {
|
|
32
|
+
const problem = await ctx.providerProblem(doc.picture.provider);
|
|
33
|
+
checks.push({
|
|
34
|
+
id: "provider",
|
|
35
|
+
ok: problem === null,
|
|
36
|
+
sentence: problem ?? "The image service is ready.",
|
|
37
|
+
fix: problem === null
|
|
38
|
+
? null
|
|
39
|
+
: { label: "Open Keys & services", href: "/settings?section=providers" },
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const kept = doc.picture.sha256 !== undefined;
|
|
44
|
+
checks.push({
|
|
45
|
+
id: "provider",
|
|
46
|
+
ok: kept,
|
|
47
|
+
sentence: kept
|
|
48
|
+
? "The template's own picture is saved with it."
|
|
49
|
+
: "This template uses its own picture, but none is saved with it. Upload one under Picture.",
|
|
50
|
+
fix: kept ? null : at("picture"),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const schedule = doc.schedule;
|
|
54
|
+
if (schedule !== undefined) {
|
|
55
|
+
const used = designFields(doc).includes(schedule.dateField);
|
|
56
|
+
checks.push({
|
|
57
|
+
id: "date-field",
|
|
58
|
+
ok: used,
|
|
59
|
+
sentence: used
|
|
60
|
+
? `Each row's date is read from {{${schedule.dateField}}}.`
|
|
61
|
+
: `The schedule reads each date from {{${schedule.dateField}}}, but the template doesn't use that keyword. Choose a keyword it uses under Schedule.`,
|
|
62
|
+
fix: used ? null : at("schedule"),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// With the AI drawing the text, Slopify draws none, so there are no fonts to check.
|
|
66
|
+
if (doc.picture.aiText)
|
|
67
|
+
return checks;
|
|
68
|
+
const fonts = new Map();
|
|
69
|
+
for (const block of doc.text.blocks)
|
|
70
|
+
if (!fonts.has(block.fontId))
|
|
71
|
+
fonts.set(block.fontId, await ctx.font(block.fontId));
|
|
72
|
+
const fontProblems = [];
|
|
73
|
+
for (const [index, block] of doc.text.blocks.entries()) {
|
|
74
|
+
const filled = render(block.text, doc.samples);
|
|
75
|
+
if (filled.trim() === "")
|
|
76
|
+
continue;
|
|
77
|
+
const where = `text block ${String(index + 1)}`;
|
|
78
|
+
const font = fonts.get(block.fontId);
|
|
79
|
+
if (font === undefined) {
|
|
80
|
+
fontProblems.push(`The font chosen for ${where} is no longer on this computer. Choose another font for it under Text layout.`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const letters = [...new Set([...filled].filter((ch) => ch.trim() !== ""))];
|
|
84
|
+
const found = mappedCodepoints(font.bytes, font.faceIndex, letters.map((ch) => ch.codePointAt(0) ?? 0));
|
|
85
|
+
const missing = found === undefined ? [] : letters.filter((ch) => !found.has(ch.codePointAt(0) ?? 0));
|
|
86
|
+
if (missing.length > 0)
|
|
87
|
+
fontProblems.push(`The font ${font.name} has no ${missing.map((ch) => `“${ch}”`).join(" ")} for the sample of ${where}. Choose another font for it under Text layout, or upload one that has ${missing.length === 1 ? "it" : "them"}.`);
|
|
88
|
+
}
|
|
89
|
+
checks.push({
|
|
90
|
+
id: "fonts",
|
|
91
|
+
ok: fontProblems.length === 0,
|
|
92
|
+
sentence: fontProblems[0] ?? "The fonts have every letter of the sample values.",
|
|
93
|
+
fix: fontProblems.length === 0 ? null : at("text"),
|
|
94
|
+
});
|
|
95
|
+
const layoutFonts = {};
|
|
96
|
+
for (const [id, font] of fonts)
|
|
97
|
+
if (font !== undefined)
|
|
98
|
+
layoutFonts[id] = { assName: font.name, advance: fontAdvances(font.bytes, font.faceIndex) };
|
|
99
|
+
const tooFull = doc.formats
|
|
100
|
+
.map((entry) => entry.format)
|
|
101
|
+
.filter((format) => layoutText(frames[format], doc.text, doc.samples, layoutFonts).overflow);
|
|
102
|
+
checks.push({
|
|
103
|
+
id: "formats",
|
|
104
|
+
ok: tooFull.length === 0,
|
|
105
|
+
sentence: tooFull.length === 0
|
|
106
|
+
? "The sample values fit on every format."
|
|
107
|
+
: `The sample text doesn't fit on ${tooFull.map((format) => formatLabels[format]).join(", ")}, even at the smallest readable size. Shorten it or choose a placement with more room under Text layout.`,
|
|
108
|
+
fix: tooFull.length === 0 ? null : at("text"),
|
|
109
|
+
});
|
|
110
|
+
return checks;
|
|
111
|
+
}
|
|
112
|
+
// Approve for schedules asks for the version's own sample, made, as Edit template does: the
|
|
113
|
+
// person has seen what a schedule will make before it makes it.
|
|
114
|
+
export function sampleCheck(db, templateId, version) {
|
|
115
|
+
const row = sampleRow(db, templateId);
|
|
116
|
+
const made = row?.state === "done" && row.version === version;
|
|
117
|
+
const how = "Make its sample, look it over beside the settings, then press Approve for schedules.";
|
|
118
|
+
return {
|
|
119
|
+
id: "sample",
|
|
120
|
+
ok: made,
|
|
121
|
+
sentence: made
|
|
122
|
+
? `Version ${String(version)}'s sample is made.`
|
|
123
|
+
: row?.state === "done" && row.version !== version
|
|
124
|
+
? `Version ${String(version)} has no sample yet: the one shown is of version ${String(row.version)}. ${how}`
|
|
125
|
+
: `Version ${String(version)} has no sample yet. ${how}`,
|
|
126
|
+
fix: made
|
|
127
|
+
? null
|
|
128
|
+
: { label: "Open Picture", href: `/library/design-templates/${templateId}#picture` },
|
|
129
|
+
};
|
|
130
|
+
}
|