@hraness/slopcamera 3.2.4
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 +21 -0
- package/NOTICE.md +60 -0
- package/PRIVACY.md +46 -0
- package/README.md +311 -0
- package/SECURITY.md +21 -0
- package/apps/desktop/README.md +966 -0
- package/apps/desktop/analysis/protocol.ts +223 -0
- package/apps/desktop/application/capability-binding.ts +594 -0
- package/apps/desktop/application/context.ts +97 -0
- package/apps/desktop/application/creative-iteration.ts +1385 -0
- package/apps/desktop/application/default-registry.ts +146 -0
- package/apps/desktop/application/directing-contract.ts +218 -0
- package/apps/desktop/application/directing-plan.ts +178 -0
- package/apps/desktop/application/errors.ts +18 -0
- package/apps/desktop/application/gateway-port.ts +867 -0
- package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
- package/apps/desktop/application/html-overlay-integrity.ts +337 -0
- package/apps/desktop/application/html-overlay-renderer.ts +45 -0
- package/apps/desktop/application/index.ts +13 -0
- package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
- package/apps/desktop/application/operation-effects.ts +94 -0
- package/apps/desktop/application/operation.ts +224 -0
- package/apps/desktop/application/operations/analysis/faces.ts +244 -0
- package/apps/desktop/application/operations/analysis/music.ts +248 -0
- package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
- package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
- package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
- package/apps/desktop/application/operations/analysis/shared.ts +202 -0
- package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
- package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
- package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
- package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
- package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
- package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
- package/apps/desktop/application/operations/gateway/image.ts +41 -0
- package/apps/desktop/application/operations/gateway/index.ts +17 -0
- package/apps/desktop/application/operations/gateway/shared.ts +122 -0
- package/apps/desktop/application/operations/gateway/speech.ts +41 -0
- package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
- package/apps/desktop/application/operations/gateway/video.ts +41 -0
- package/apps/desktop/application/operations/index.ts +31 -0
- package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
- package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
- package/apps/desktop/application/operations/iteration/select.ts +155 -0
- package/apps/desktop/application/operations/iteration/shared.ts +250 -0
- package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
- package/apps/desktop/application/operations/media/binding.ts +79 -0
- package/apps/desktop/application/operations/media/capabilities.ts +112 -0
- package/apps/desktop/application/operations/media/color-grade.ts +343 -0
- package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
- package/apps/desktop/application/operations/media/index.ts +8 -0
- package/apps/desktop/application/operations/media/ingest.ts +303 -0
- package/apps/desktop/application/operations/media/overlay.ts +729 -0
- package/apps/desktop/application/operations/media/shared.ts +845 -0
- package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
- package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
- package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
- package/apps/desktop/application/operations/project/snapshot.ts +95 -0
- package/apps/desktop/application/operations/recording/index.ts +5 -0
- package/apps/desktop/application/operations/recording/pause.ts +34 -0
- package/apps/desktop/application/operations/recording/resume.ts +34 -0
- package/apps/desktop/application/operations/recording/shared.ts +199 -0
- package/apps/desktop/application/operations/recording/start.ts +169 -0
- package/apps/desktop/application/operations/recording/stop.ts +34 -0
- package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
- package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
- package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
- package/apps/desktop/application/operations/render/project-plan.ts +939 -0
- package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
- package/apps/desktop/application/operations/render/project.ts +1922 -0
- package/apps/desktop/application/operations/shared.ts +67 -0
- package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
- package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
- package/apps/desktop/application/operations/spatial-project.ts +209 -0
- package/apps/desktop/application/operations/spatial-render.ts +415 -0
- package/apps/desktop/application/operations/spatial-scene.ts +97 -0
- package/apps/desktop/application/operations/studio.ts +143 -0
- package/apps/desktop/application/output-publication-lease.ts +163 -0
- package/apps/desktop/application/project-publication-lease.ts +233 -0
- package/apps/desktop/application/project-store.ts +196 -0
- package/apps/desktop/application/receipts.ts +533 -0
- package/apps/desktop/application/registry.ts +182 -0
- package/apps/desktop/application/spatial-assets.ts +675 -0
- package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
- package/apps/desktop/application/spatial-project-authority.ts +35 -0
- package/apps/desktop/application/spatial-project-lease.ts +126 -0
- package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
- package/apps/desktop/application/spatial-project-store.ts +286 -0
- package/apps/desktop/application/spatial-render-projection.ts +201 -0
- package/apps/desktop/application/spatial-render.ts +677 -0
- package/apps/desktop/application/spatial-spz.ts +44 -0
- package/apps/desktop/application/spatial-world-import.ts +135 -0
- package/apps/desktop/application/spatial-world-provenance.ts +46 -0
- package/apps/desktop/application/studio-port.ts +58 -0
- package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
- package/apps/desktop/capture/protocol.ts +689 -0
- package/apps/desktop/cli/analyzer.ts +406 -0
- package/apps/desktop/cli/args.ts +3181 -0
- package/apps/desktop/cli/asset-ingest.ts +747 -0
- package/apps/desktop/cli/atomic-render-effects.ts +210 -0
- package/apps/desktop/cli/atomic-render-platform.ts +205 -0
- package/apps/desktop/cli/atomic-render.ts +50 -0
- package/apps/desktop/cli/audio-analysis.ts +252 -0
- package/apps/desktop/cli/bounded-map.ts +39 -0
- package/apps/desktop/cli/bundle-service.ts +256 -0
- package/apps/desktop/cli/capabilities.ts +212 -0
- package/apps/desktop/cli/capture-bundle.ts +1117 -0
- package/apps/desktop/cli/command-host-resources.ts +251 -0
- package/apps/desktop/cli/commands.ts +6836 -0
- package/apps/desktop/cli/directing-blob.ts +291 -0
- package/apps/desktop/cli/directing-media.ts +517 -0
- package/apps/desktop/cli/directing-quote.ts +89 -0
- package/apps/desktop/cli/directing-service.ts +288 -0
- package/apps/desktop/cli/emoji-assets.ts +538 -0
- package/apps/desktop/cli/errors.ts +29 -0
- package/apps/desktop/cli/face-analysis-service.ts +940 -0
- package/apps/desktop/cli/filter-script.ts +76 -0
- package/apps/desktop/cli/gateway-application-port.ts +1142 -0
- package/apps/desktop/cli/gateway-credential.ts +81 -0
- package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
- package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
- package/apps/desktop/cli/gateway-media-service.ts +3106 -0
- package/apps/desktop/cli/gateway-media-signature.ts +122 -0
- package/apps/desktop/cli/gateway-provider-options.ts +562 -0
- package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
- package/apps/desktop/cli/help.ts +501 -0
- package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
- package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
- package/apps/desktop/cli/io.ts +290 -0
- package/apps/desktop/cli/main.ts +123 -0
- package/apps/desktop/cli/media-effects-service.ts +885 -0
- package/apps/desktop/cli/media-ingest-model.ts +364 -0
- package/apps/desktop/cli/media-ingest-platform.ts +227 -0
- package/apps/desktop/cli/media-ingest-program.ts +200 -0
- package/apps/desktop/cli/media-ingest.ts +30 -0
- package/apps/desktop/cli/music-analysis-service.ts +434 -0
- package/apps/desktop/cli/mutation-lock.ts +530 -0
- package/apps/desktop/cli/native-assets.d.ts +12 -0
- package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
- package/apps/desktop/cli/overlay-playback.ts +177 -0
- package/apps/desktop/cli/paths.ts +265 -0
- package/apps/desktop/cli/portable-surface.ts +242 -0
- package/apps/desktop/cli/project-camera-receipt.ts +112 -0
- package/apps/desktop/cli/project-face-camera.ts +394 -0
- package/apps/desktop/cli/project-inactivity-service.ts +552 -0
- package/apps/desktop/cli/project-media-integrity.ts +121 -0
- package/apps/desktop/cli/project-renderer.ts +1526 -0
- package/apps/desktop/cli/project-service.ts +519 -0
- package/apps/desktop/cli/project-state-transaction.ts +520 -0
- package/apps/desktop/cli/query-limits.ts +1 -0
- package/apps/desktop/cli/recording-controller.ts +1567 -0
- package/apps/desktop/cli/recording-daemon.ts +931 -0
- package/apps/desktop/cli/recording-ref.ts +56 -0
- package/apps/desktop/cli/renamed-environment.ts +6 -0
- package/apps/desktop/cli/renderer.ts +1439 -0
- package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
- package/apps/desktop/cli/scene-analysis-service.ts +879 -0
- package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
- package/apps/desktop/cli/spatial-project-service.ts +90 -0
- package/apps/desktop/cli/spatial-scene-service.ts +105 -0
- package/apps/desktop/cli/spatial-world-service.ts +39 -0
- package/apps/desktop/cli/speech-analysis-service.ts +682 -0
- package/apps/desktop/cli/studio-assemble.ts +58 -0
- package/apps/desktop/cli/studio-command.ts +60 -0
- package/apps/desktop/cli/studio-custody.ts +60 -0
- package/apps/desktop/cli/studio-encode.ts +216 -0
- package/apps/desktop/cli/studio-exr.ts +52 -0
- package/apps/desktop/cli/studio-files.ts +179 -0
- package/apps/desktop/cli/studio-output-validation.ts +162 -0
- package/apps/desktop/cli/studio-process.ts +145 -0
- package/apps/desktop/cli/studio-runtime.ts +77 -0
- package/apps/desktop/cli/studio-scaffold.ts +70 -0
- package/apps/desktop/cli/studio-service.ts +301 -0
- package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
- package/apps/desktop/cli/studio-template-names.ts +3 -0
- package/apps/desktop/cli/studio-workflow.ts +19 -0
- package/apps/desktop/cli/time.ts +25 -0
- package/apps/desktop/cli/workflow-code.ts +584 -0
- package/apps/desktop/cli/workflow-runs.ts +576 -0
- package/apps/desktop/code/advanced.ts +139 -0
- package/apps/desktop/code/application-node-planner.ts +1288 -0
- package/apps/desktop/code/compiler.ts +329 -0
- package/apps/desktop/code/contracts.ts +13 -0
- package/apps/desktop/code/creative-recipes.ts +94 -0
- package/apps/desktop/code/define-workflow.ts +115 -0
- package/apps/desktop/code/editing.ts +284 -0
- package/apps/desktop/code/file-candidate-provenance.ts +420 -0
- package/apps/desktop/code/file-candidate.ts +69 -0
- package/apps/desktop/code/graph-builder.ts +10 -0
- package/apps/desktop/code/host-resource-policy.ts +55 -0
- package/apps/desktop/code/plan-contracts.ts +159 -0
- package/apps/desktop/code/planning.ts +361 -0
- package/apps/desktop/code/public.ts +134 -0
- package/apps/desktop/code/reference-projection.ts +62 -0
- package/apps/desktop/code/run-contracts.ts +466 -0
- package/apps/desktop/code/run-store.ts +2478 -0
- package/apps/desktop/code/runtime-identity.ts +328 -0
- package/apps/desktop/code/scheduler.ts +3067 -0
- package/apps/desktop/code/semantic-builder.ts +1436 -0
- package/apps/desktop/code/source-bundle.ts +1641 -0
- package/apps/desktop/code/source-typecheck.ts +211 -0
- package/apps/desktop/code/testing.ts +79 -0
- package/apps/desktop/code/worker-client.ts +1675 -0
- package/apps/desktop/code/worker-effects.ts +141 -0
- package/apps/desktop/code/worker-entry.ts +435 -0
- package/apps/desktop/code/worker-lease-guardian.ts +179 -0
- package/apps/desktop/code/worker-process-identity.ts +484 -0
- package/apps/desktop/code/worker-protocol.ts +211 -0
- package/apps/desktop/code/workflow-effects.ts +132 -0
- package/apps/desktop/contracts/analysis.ts +949 -0
- package/apps/desktop/contracts/edit.ts +576 -0
- package/apps/desktop/contracts/index.ts +9 -0
- package/apps/desktop/contracts/media-effects.ts +139 -0
- package/apps/desktop/contracts/project-render.ts +644 -0
- package/apps/desktop/contracts/project.ts +740 -0
- package/apps/desktop/contracts/recording.ts +1434 -0
- package/apps/desktop/contracts/render.ts +334 -0
- package/apps/desktop/contracts/runtime.ts +155 -0
- package/apps/desktop/contracts/spatial-compositor.ts +58 -0
- package/apps/desktop/contracts/spatial-project.ts +159 -0
- package/apps/desktop/contracts/spatial-world.ts +48 -0
- package/apps/desktop/contracts/video-effects.ts +120 -0
- package/apps/desktop/core/alignment-apply.ts +400 -0
- package/apps/desktop/core/audio-alignment.ts +466 -0
- package/apps/desktop/core/auto-zoom.ts +209 -0
- package/apps/desktop/core/canonical-json.ts +14 -0
- package/apps/desktop/core/emoji.ts +210 -0
- package/apps/desktop/core/events.ts +409 -0
- package/apps/desktop/core/face-framing.ts +533 -0
- package/apps/desktop/core/face-tracks.ts +326 -0
- package/apps/desktop/core/inactivity.ts +137 -0
- package/apps/desktop/core/index.ts +30 -0
- package/apps/desktop/core/intervals.ts +123 -0
- package/apps/desktop/core/montage.ts +236 -0
- package/apps/desktop/core/music-analysis.ts +518 -0
- package/apps/desktop/core/plan.ts +244 -0
- package/apps/desktop/core/project-auto-zoom.ts +75 -0
- package/apps/desktop/core/project-camera-spatial.ts +676 -0
- package/apps/desktop/core/project-camera.ts +403 -0
- package/apps/desktop/core/project-captions.ts +888 -0
- package/apps/desktop/core/project-inactivity.ts +125 -0
- package/apps/desktop/core/project-metadata.ts +927 -0
- package/apps/desktop/core/project-plan.ts +375 -0
- package/apps/desktop/core/project-render-plan.ts +303 -0
- package/apps/desktop/core/project-time.ts +469 -0
- package/apps/desktop/core/render-plan.ts +442 -0
- package/apps/desktop/core/scene-cache.ts +174 -0
- package/apps/desktop/core/scene-sampling.ts +448 -0
- package/apps/desktop/core/spatial-compositor.ts +22 -0
- package/apps/desktop/core/spatial-durability.ts +99 -0
- package/apps/desktop/core/spatial-project.ts +169 -0
- package/apps/desktop/core/spatial-shot-clock.ts +32 -0
- package/apps/desktop/core/speech-analysis.ts +333 -0
- package/apps/desktop/core/storage.ts +970 -0
- package/apps/desktop/core/time-map.ts +91 -0
- package/apps/desktop/core/time.ts +61 -0
- package/apps/desktop/core/video-effects.ts +660 -0
- package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
- package/apps/desktop/dist/cli/main.js +5086 -0
- package/apps/desktop/html-overlay/catalog.ts +171 -0
- package/apps/desktop/html-overlay/contracts.ts +372 -0
- package/apps/desktop/html-overlay/execution-profile.ts +77 -0
- package/apps/desktop/html-overlay/index.ts +98 -0
- package/apps/desktop/html-overlay/libraries.ts +263 -0
- package/apps/desktop/html-overlay/random.ts +49 -0
- package/apps/desktop/html-overlay/runtime.ts +813 -0
- package/apps/desktop/html-overlay/scaffolds.ts +792 -0
- package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
- package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
- package/apps/desktop/html-overlay/spatial.ts +720 -0
- package/apps/desktop/studio/assets/README.md +39 -0
- package/apps/desktop/studio/assets/contracts.ts +129 -0
- package/apps/desktop/studio/assets/network.ts +64 -0
- package/apps/desktop/studio/assets/poly-haven.ts +148 -0
- package/apps/desktop/studio/drivers/blender_driver.py +466 -0
- package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
- package/apps/desktop/studio/education/driver.py +308 -0
- package/apps/desktop/studio/education/lesson.py +180 -0
- package/apps/desktop/studio/education/toolkit.py +249 -0
- package/apps/desktop/workflows/chaptered-demo.ts +48 -0
- package/apps/desktop/workflows/creative-iteration.ts +107 -0
- package/apps/desktop/workflows/creative-selection.ts +92 -0
- package/apps/desktop/workflows/fragments.ts +486 -0
- package/apps/desktop/workflows/index.ts +93 -0
- package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
- package/apps/desktop/workflows/scene-direction.ts +12 -0
- package/apps/desktop/workflows/social-variants.ts +75 -0
- package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
- package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
- package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
- package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
- package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
- package/dist/cli.js +479 -0
- package/dist/code/advanced.js +270 -0
- package/dist/code/index.js +2240 -0
- package/dist/generate.js +24 -0
- package/dist/host-resources.js +26 -0
- package/dist/index-42zsesc1.js +2257 -0
- package/dist/index-7308egqr.js +48 -0
- package/dist/index-8txs6fkn.js +712 -0
- package/dist/index-mcy8z0br.js +192 -0
- package/dist/index-p63wavx0.js +2029 -0
- package/dist/index-r7gdhmsp.js +424 -0
- package/dist/index-sh6xbav6.js +1219 -0
- package/dist/index-z1w83f81.js +4 -0
- package/dist/index-z7239b4h.js +2363 -0
- package/dist/index-zfeg9hmw.js +2013 -0
- package/dist/index.js +241 -0
- package/dist/operations.js +29 -0
- package/dist/skill-install-dh0ntqba.js +10 -0
- package/dist/vectorize/worker.js +142 -0
- package/dist/workflow.js +16 -0
- package/docs/README.md +38 -0
- package/docs/studio.md +215 -0
- package/examples/studio/blender/README.md +26 -0
- package/examples/studio/blender/character.py +152 -0
- package/examples/studio/blender/cloth.py +101 -0
- package/examples/studio/blender/color_chart.py +29 -0
- package/examples/studio/blender/fluid.py +78 -0
- package/examples/studio/blender/import_model.py +74 -0
- package/examples/studio/blender/product.py +53 -0
- package/examples/studio/blender/shaded_street.py +481 -0
- package/examples/studio/blender/studio_scene.py +147 -0
- package/examples/studio/cadquery/README.md +13 -0
- package/examples/studio/cadquery/bracket.py +36 -0
- package/examples/studio/cadquery/import_step.py +12 -0
- package/examples/studio/education/README.md +46 -0
- package/examples/studio/education/lesson.json +99 -0
- package/examples/studio/education/scene.py +7 -0
- package/examples/studio/hybrid-scene.ts +96 -0
- package/examples/studio/native-workflow.ts +14 -0
- package/examples/studio/vgpu/README.md +64 -0
- package/examples/studio/vgpu/heat-field.mjs +290 -0
- package/package.json +304 -0
- package/packages/scene/src/index.ts +1 -0
- package/packages/scene/src/scene.ts +215 -0
- package/schema/diagram.schema.json +334 -0
- package/skills/slopcamera/SKILL.md +45 -0
- package/skills/slopcamera/agents/openai.yaml +4 -0
- package/skills/slopcamera/references/customization.md +77 -0
- package/skills/slopcamera/references/diagrams.md +153 -0
- package/skills/slopcamera/references/directed-scenes.md +42 -0
- package/skills/slopcamera/references/directing-video.md +18 -0
- package/skills/slopcamera/references/educational-video.md +26 -0
- package/skills/slopcamera/references/gateway-media.md +134 -0
- package/skills/slopcamera/references/install.md +62 -0
- package/skills/slopcamera/references/native-studio.md +31 -0
- package/skills/slopcamera/references/reference-led-3d.md +140 -0
- package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
- package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
- package/skills/slopcamera/references/vectorization.md +29 -0
- package/skills/slopcamera/references/video-projects.md +187 -0
- package/skills/slopcamera/references/visual-communication.md +120 -0
- package/skills/slopcamera/references/workflows-sdk.md +45 -0
- package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
- package/src/artifacts.ts +106 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
- package/src/assets.d.ts +9 -0
- package/src/cli.ts +601 -0
- package/src/cloud-errors.ts +26 -0
- package/src/code/advanced.ts +10 -0
- package/src/code/boundary.ts +21 -0
- package/src/code/canonical-json.ts +129 -0
- package/src/code/compiler.ts +953 -0
- package/src/code/contracts.ts +576 -0
- package/src/code/define-workflow.ts +334 -0
- package/src/code/errors.ts +41 -0
- package/src/code/graph-builder.ts +945 -0
- package/src/code/index.ts +121 -0
- package/src/code/json-snapshot.ts +899 -0
- package/src/code/json-utf8.ts +88 -0
- package/src/code/portable-builder.ts +136 -0
- package/src/code/projection.ts +378 -0
- package/src/code/public-operations.ts +450 -0
- package/src/code/runtime.ts +580 -0
- package/src/code/sha256.ts +36 -0
- package/src/config.ts +180 -0
- package/src/desktop.ts +253 -0
- package/src/fs.ts +10 -0
- package/src/generate.ts +704 -0
- package/src/host-resource-posix.ts +161 -0
- package/src/host-resources.ts +1749 -0
- package/src/icons.ts +58 -0
- package/src/index.ts +200 -0
- package/src/label-layout.ts +130 -0
- package/src/layout.ts +413 -0
- package/src/lint.ts +127 -0
- package/src/mcp/boundary.ts +354 -0
- package/src/mcp/index.ts +33 -0
- package/src/mcp/server.ts +272 -0
- package/src/mcp/tools.ts +963 -0
- package/src/mcp/types.ts +71 -0
- package/src/operations.ts +775 -0
- package/src/parse.ts +861 -0
- package/src/process-environment.ts +17 -0
- package/src/render.ts +437 -0
- package/src/skill-install.ts +49 -0
- package/src/spatial-scene/authoring.ts +27 -0
- package/src/spatial-scene/camera-track.ts +83 -0
- package/src/spatial-scene/contracts.ts +277 -0
- package/src/spatial-scene/evaluate.ts +145 -0
- package/src/spatial-scene/gltf.ts +558 -0
- package/src/spatial-scene/identity.ts +260 -0
- package/src/spatial-scene/index.ts +10 -0
- package/src/spatial-scene/inspect.ts +68 -0
- package/src/spatial-scene/math.ts +280 -0
- package/src/spatial-scene/patch.ts +131 -0
- package/src/spatial-scene/time.ts +53 -0
- package/src/studio/contracts.ts +129 -0
- package/src/studio/index.ts +2 -0
- package/src/studio/plan.ts +156 -0
- package/src/studio/shared.ts +41 -0
- package/src/theme.ts +68 -0
- package/src/tldr.ts +474 -0
- package/src/types.ts +212 -0
- package/src/vectorize/archive.ts +101 -0
- package/src/vectorize/command.ts +797 -0
- package/src/vectorize/index.ts +27 -0
- package/src/vectorize/limits.ts +61 -0
- package/src/vectorize/metrics.ts +351 -0
- package/src/vectorize/pixels.ts +323 -0
- package/src/vectorize/supervisor.ts +339 -0
- package/src/vectorize/svg.ts +188 -0
- package/src/vectorize/tool.ts +542 -0
- package/src/vectorize/types.ts +113 -0
- package/src/vectorize/vectorize.ts +634 -0
- package/src/vectorize/worker-protocol.ts +40 -0
- package/src/vectorize/worker.ts +209 -0
- package/src/version.ts +1 -0
- package/src/workflow.ts +409 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EditPlanV1Schema,
|
|
3
|
+
type EditPlanV1,
|
|
4
|
+
type SourceInterval,
|
|
5
|
+
type ZoomTarget,
|
|
6
|
+
} from "../contracts/edit";
|
|
7
|
+
import {
|
|
8
|
+
RecordingManifestV1Schema,
|
|
9
|
+
type Point,
|
|
10
|
+
type RecordingEventV1,
|
|
11
|
+
type RecordingManifestV1,
|
|
12
|
+
type Rect,
|
|
13
|
+
type TrackId,
|
|
14
|
+
} from "../contracts/recording";
|
|
15
|
+
import { RenderPlanV1Schema, type RenderPlanV1, type RenderTypingSpan } from "../contracts/render";
|
|
16
|
+
import { deriveTypingSpans, findCursorAtTime, findFocusedInputAtTime, findWindowAtTime, smoothAndDownsampleCursor } from "./events";
|
|
17
|
+
import { hashEditPlan } from "./plan";
|
|
18
|
+
import { buildSourceTimeMap, mapSourceInterval, sourceToOutputUs } from "./time-map";
|
|
19
|
+
|
|
20
|
+
export interface RenderCompileOptions {
|
|
21
|
+
readonly audioTrackIds: readonly TrackId[];
|
|
22
|
+
readonly camera: Readonly<{ kind: "none" }>;
|
|
23
|
+
readonly displayTrackId: TrackId;
|
|
24
|
+
readonly frameRate: number;
|
|
25
|
+
readonly pixelHeight: number;
|
|
26
|
+
readonly pixelWidth: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface DisplayTransform {
|
|
30
|
+
readonly bounds: Rect;
|
|
31
|
+
readonly displayId: string;
|
|
32
|
+
readonly outputScaleX: number;
|
|
33
|
+
readonly outputScaleY: number;
|
|
34
|
+
readonly scaleFactor: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface ResolvedZoomTarget {
|
|
38
|
+
readonly scale: number;
|
|
39
|
+
readonly viewport: Rect;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class UnsatisfiedRenderTargetError extends Error {
|
|
43
|
+
readonly sourceTimeUs: number;
|
|
44
|
+
readonly target: ZoomTarget;
|
|
45
|
+
|
|
46
|
+
constructor(target: ZoomTarget, sourceTimeUs: number) {
|
|
47
|
+
super(`Cannot resolve ${target.kind} zoom target at ${sourceTimeUs}us.`);
|
|
48
|
+
this.name = "UnsatisfiedRenderTargetError";
|
|
49
|
+
this.sourceTimeUs = sourceTimeUs;
|
|
50
|
+
this.target = target;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function clamp(value: number, minimum: number, maximum: number): number {
|
|
55
|
+
return Math.min(maximum, Math.max(minimum, value));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function targetViewport(
|
|
59
|
+
target: Rect,
|
|
60
|
+
requestedScale: number,
|
|
61
|
+
options: RenderCompileOptions,
|
|
62
|
+
paddingPx = 0,
|
|
63
|
+
): ResolvedZoomTarget {
|
|
64
|
+
const aspect = options.pixelWidth / options.pixelHeight;
|
|
65
|
+
const requestedWidth = options.pixelWidth / requestedScale;
|
|
66
|
+
const paddedWidth = Math.min(options.pixelWidth, target.width + paddingPx * 2);
|
|
67
|
+
const paddedHeight = Math.min(options.pixelHeight, target.height + paddingPx * 2);
|
|
68
|
+
const width = Math.min(
|
|
69
|
+
options.pixelWidth,
|
|
70
|
+
Math.max(requestedWidth, paddedWidth, paddedHeight * aspect),
|
|
71
|
+
);
|
|
72
|
+
const height = width / aspect;
|
|
73
|
+
const x = clamp(target.x + target.width / 2 - width / 2, 0, options.pixelWidth - width);
|
|
74
|
+
const y = clamp(target.y + target.height / 2 - height / 2, 0, options.pixelHeight - height);
|
|
75
|
+
return {
|
|
76
|
+
scale: options.pixelWidth / width,
|
|
77
|
+
viewport: { height, width, x, y },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function pointViewport(point: Point, scale: number, options: RenderCompileOptions): ResolvedZoomTarget {
|
|
82
|
+
return targetViewport({ height: 0, width: 0, x: point.x, y: point.y }, scale, options);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function pointInRect(point: Point, rect: Rect): boolean {
|
|
86
|
+
return point.x >= rect.x && point.x <= rect.x + rect.width && point.y >= rect.y && point.y <= rect.y + rect.height;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function globalPointToOutput(point: Point, transform: DisplayTransform): Point {
|
|
90
|
+
if (!pointInRect(point, transform.bounds)) {
|
|
91
|
+
throw new Error(`Global point (${point.x}, ${point.y}) is outside selected display ${transform.displayId}.`);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
x: (point.x - transform.bounds.x) * transform.scaleFactor * transform.outputScaleX,
|
|
95
|
+
y: (point.y - transform.bounds.y) * transform.scaleFactor * transform.outputScaleY,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function globalRectToOutput(rect: Rect, transform: DisplayTransform): Rect {
|
|
100
|
+
const left = Math.max(rect.x, transform.bounds.x);
|
|
101
|
+
const top = Math.max(rect.y, transform.bounds.y);
|
|
102
|
+
const right = Math.min(rect.x + rect.width, transform.bounds.x + transform.bounds.width);
|
|
103
|
+
const bottom = Math.min(rect.y + rect.height, transform.bounds.y + transform.bounds.height);
|
|
104
|
+
if (right <= left || bottom <= top) {
|
|
105
|
+
throw new Error(`Global rect does not intersect selected display ${transform.displayId}.`);
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
height: (bottom - top) * transform.scaleFactor * transform.outputScaleY,
|
|
109
|
+
width: (right - left) * transform.scaleFactor * transform.outputScaleX,
|
|
110
|
+
x: (left - transform.bounds.x) * transform.scaleFactor * transform.outputScaleX,
|
|
111
|
+
y: (top - transform.bounds.y) * transform.scaleFactor * transform.outputScaleY,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function tryGlobalRectToOutput(rect: Rect, transform: DisplayTransform): Rect | null {
|
|
116
|
+
try {
|
|
117
|
+
return globalRectToOutput(rect, transform);
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function resolveTarget(
|
|
124
|
+
target: ZoomTarget,
|
|
125
|
+
sourceTimeUs: number,
|
|
126
|
+
scale: number,
|
|
127
|
+
events: readonly RecordingEventV1[],
|
|
128
|
+
options: RenderCompileOptions,
|
|
129
|
+
transform: DisplayTransform,
|
|
130
|
+
): ResolvedZoomTarget {
|
|
131
|
+
if (target.kind === "rect") return targetViewport(globalRectToOutput(target.rect, transform), scale, options);
|
|
132
|
+
if (target.kind === "point") return pointViewport(globalPointToOutput(target.point, transform), scale, options);
|
|
133
|
+
if (target.kind === "cursor") {
|
|
134
|
+
const cursor = findCursorAtTime(
|
|
135
|
+
events.filter((event) => event.type !== "cursor.sample" || event.displayId === transform.displayId),
|
|
136
|
+
sourceTimeUs,
|
|
137
|
+
target.sampling,
|
|
138
|
+
);
|
|
139
|
+
if (cursor !== null && cursor.visible) {
|
|
140
|
+
return pointViewport(globalPointToOutput(cursor.position, transform), scale, options);
|
|
141
|
+
}
|
|
142
|
+
} else if (target.kind === "window") {
|
|
143
|
+
const window = findWindowAtTime(events, target.selector, sourceTimeUs);
|
|
144
|
+
if (window !== null) {
|
|
145
|
+
return targetViewport(globalRectToOutput(window.bounds, transform), scale, options, target.paddingPx);
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
const input = findFocusedInputAtTime(events, sourceTimeUs);
|
|
149
|
+
if (input !== null) {
|
|
150
|
+
return targetViewport(globalRectToOutput(input.bounds, transform), scale, options, target.paddingPx);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
throw new UnsatisfiedRenderTargetError(target, sourceTimeUs);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function zoomScaleAt(
|
|
157
|
+
sourceTimeUs: number,
|
|
158
|
+
zoom: EditPlanV1["zooms"][number],
|
|
159
|
+
): number {
|
|
160
|
+
const enterEndUs = zoom.range.startUs + zoom.enterDurationUs;
|
|
161
|
+
const exitStartUs = zoom.range.endUs - zoom.exitDurationUs;
|
|
162
|
+
if (zoom.enterDurationUs > 0 && sourceTimeUs < enterEndUs) {
|
|
163
|
+
return 1 + (zoom.scale - 1) * ((sourceTimeUs - zoom.range.startUs) / zoom.enterDurationUs);
|
|
164
|
+
}
|
|
165
|
+
if (zoom.exitDurationUs > 0 && sourceTimeUs > exitStartUs) {
|
|
166
|
+
return 1 + (zoom.scale - 1) * ((zoom.range.endUs - sourceTimeUs) / zoom.exitDurationUs);
|
|
167
|
+
}
|
|
168
|
+
return zoom.scale;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function keyframeSourceTimes(
|
|
172
|
+
part: SourceInterval,
|
|
173
|
+
zoom: EditPlanV1["zooms"][number],
|
|
174
|
+
): readonly number[] {
|
|
175
|
+
const candidates = [
|
|
176
|
+
part.startUs,
|
|
177
|
+
zoom.range.startUs + zoom.enterDurationUs,
|
|
178
|
+
zoom.range.endUs - zoom.exitDurationUs,
|
|
179
|
+
part.endUs,
|
|
180
|
+
];
|
|
181
|
+
return [...new Set(candidates.filter((timeUs) => timeUs >= part.startUs && timeUs <= part.endUs))]
|
|
182
|
+
.sort((left, right) => left - right);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function compileRenderPlan(
|
|
186
|
+
manifestInput: RecordingManifestV1,
|
|
187
|
+
planInput: EditPlanV1,
|
|
188
|
+
events: readonly RecordingEventV1[],
|
|
189
|
+
options: RenderCompileOptions,
|
|
190
|
+
): RenderPlanV1 {
|
|
191
|
+
const manifest = RecordingManifestV1Schema.parse(manifestInput);
|
|
192
|
+
const plan = EditPlanV1Schema.parse(planInput);
|
|
193
|
+
if (manifest.recordingId !== plan.recordingId) throw new Error("Edit plan belongs to a different recording.");
|
|
194
|
+
if (manifest.timeline.durationUs !== plan.sourceDurationUs) {
|
|
195
|
+
throw new Error("Edit plan source duration differs from its recording manifest.");
|
|
196
|
+
}
|
|
197
|
+
if (
|
|
198
|
+
!Number.isSafeInteger(options.pixelWidth)
|
|
199
|
+
|| options.pixelWidth <= 0
|
|
200
|
+
|| options.pixelWidth > 16_384
|
|
201
|
+
|| !Number.isSafeInteger(options.pixelHeight)
|
|
202
|
+
|| options.pixelHeight <= 0
|
|
203
|
+
|| options.pixelHeight > 16_384
|
|
204
|
+
|| options.pixelWidth * options.pixelHeight > 134_217_728
|
|
205
|
+
|| !Number.isFinite(options.frameRate)
|
|
206
|
+
|| options.frameRate <= 0
|
|
207
|
+
|| options.frameRate > 240
|
|
208
|
+
) {
|
|
209
|
+
throw new RangeError("Invalid render output dimensions or frame rate.");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const timeMap = buildSourceTimeMap(plan);
|
|
213
|
+
const displayTrack = manifest.tracks.find((track) => track.trackId === options.displayTrackId);
|
|
214
|
+
if (displayTrack?.kind !== "display-video") throw new Error("displayTrackId must select a display-video track.");
|
|
215
|
+
const display = manifest.sources.displays.find(({ displayId }) => displayId === displayTrack.source.displayId);
|
|
216
|
+
if (display === undefined) throw new Error("Selected display track has no source inventory entry.");
|
|
217
|
+
const audioTrackIdSet = new Set(options.audioTrackIds);
|
|
218
|
+
for (const trackId of audioTrackIdSet) {
|
|
219
|
+
const track = manifest.tracks.find((candidate) => candidate.trackId === trackId);
|
|
220
|
+
if (track === undefined || (track.kind !== "system-audio" && track.kind !== "microphone-audio")) {
|
|
221
|
+
throw new Error(`Audio selection contains a non-audio track: ${trackId}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const camera = options.camera;
|
|
225
|
+
const selectedTrackIds = new Set<TrackId>([
|
|
226
|
+
options.displayTrackId,
|
|
227
|
+
...options.audioTrackIds,
|
|
228
|
+
]);
|
|
229
|
+
const transform: DisplayTransform = {
|
|
230
|
+
bounds: display.bounds,
|
|
231
|
+
displayId: display.displayId,
|
|
232
|
+
outputScaleX: options.pixelWidth / display.pixelSize.width,
|
|
233
|
+
outputScaleY: options.pixelHeight / display.pixelSize.height,
|
|
234
|
+
scaleFactor: display.scaleFactor,
|
|
235
|
+
};
|
|
236
|
+
const sourceSegments = manifest.tracks.filter(({ trackId }) => selectedTrackIds.has(trackId))
|
|
237
|
+
.flatMap((track) => track.segments.flatMap((segment) => {
|
|
238
|
+
const integrity = segment.integrity;
|
|
239
|
+
if (integrity.state !== "verified" || integrity.bytes <= 0) {
|
|
240
|
+
throw new Error(`Render source segment ${segment.segmentId} has not passed integrity verification.`);
|
|
241
|
+
}
|
|
242
|
+
return mapSourceInterval(timeMap, { startUs: segment.startUs, endUs: segment.endUs })
|
|
243
|
+
.map(({ source, output }) => ({
|
|
244
|
+
bytes: integrity.bytes,
|
|
245
|
+
codec: segment.codec,
|
|
246
|
+
container: segment.container,
|
|
247
|
+
containerTrackIdentity: segment.containerTrackIdentity,
|
|
248
|
+
kind: track.kind,
|
|
249
|
+
output,
|
|
250
|
+
path: segment.path,
|
|
251
|
+
sha256: integrity.sha256,
|
|
252
|
+
source,
|
|
253
|
+
streamIndex: segment.streamIndex,
|
|
254
|
+
trackId: track.trackId,
|
|
255
|
+
}));
|
|
256
|
+
}));
|
|
257
|
+
|
|
258
|
+
const cameraKeyframes = plan.zooms.filter((zoom) => zoom.displayId === display.displayId)
|
|
259
|
+
.flatMap((zoom) => mapSourceInterval(timeMap, zoom.range).flatMap(({ source }) => (
|
|
260
|
+
keyframeSourceTimes(source, zoom).map((sourceTimeUs) => {
|
|
261
|
+
const outputTimeUs = sourceToOutputUs(timeMap, sourceTimeUs);
|
|
262
|
+
if (outputTimeUs === null) throw new UnsatisfiedRenderTargetError(zoom.target, sourceTimeUs);
|
|
263
|
+
const target = resolveTarget(
|
|
264
|
+
zoom.target,
|
|
265
|
+
sourceTimeUs,
|
|
266
|
+
zoomScaleAt(sourceTimeUs, zoom),
|
|
267
|
+
events,
|
|
268
|
+
options,
|
|
269
|
+
transform,
|
|
270
|
+
);
|
|
271
|
+
return {
|
|
272
|
+
easing: zoom.easing,
|
|
273
|
+
outputTimeUs,
|
|
274
|
+
scale: target.scale,
|
|
275
|
+
sourceTimeUs,
|
|
276
|
+
viewport: target.viewport,
|
|
277
|
+
zoomId: zoom.zoomId,
|
|
278
|
+
};
|
|
279
|
+
})
|
|
280
|
+
))).sort((left, right) => left.outputTimeUs - right.outputTimeUs || left.zoomId.localeCompare(right.zoomId));
|
|
281
|
+
|
|
282
|
+
const overlays = plan.overlays.flatMap((operation) => {
|
|
283
|
+
const mapped = mapSourceInterval(timeMap, operation.range);
|
|
284
|
+
const first = mapped[0];
|
|
285
|
+
const last = mapped.at(-1);
|
|
286
|
+
if (first === undefined || last === undefined) return [];
|
|
287
|
+
// An overlay is an output-timeline layer. Speed and keep boundaries split
|
|
288
|
+
// source media, but all retained pieces of one continuous overlay range
|
|
289
|
+
// abut after cuts are removed. Coalescing them preserves one playback and
|
|
290
|
+
// entrance/exit clock instead of restarting the asset at every boundary.
|
|
291
|
+
return [{
|
|
292
|
+
operation,
|
|
293
|
+
output: { endUs: last.output.endUs, startUs: first.output.startUs },
|
|
294
|
+
}];
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const cursorSamples = plan.effects.cursor.enabled
|
|
298
|
+
? smoothAndDownsampleCursor(events, {
|
|
299
|
+
minDistancePx: 0.5,
|
|
300
|
+
minIntervalUs: Math.max(1, Math.round(1_000_000 / options.frameRate)),
|
|
301
|
+
strength: plan.effects.cursor.smoothing.algorithm === "none"
|
|
302
|
+
? 0
|
|
303
|
+
: plan.effects.cursor.smoothing.strength,
|
|
304
|
+
}).filter((sample) => sample.displayId === display.displayId).flatMap((sample) => {
|
|
305
|
+
const outputTimeUs = sourceToOutputUs(timeMap, sample.sourceTimeUs);
|
|
306
|
+
return outputTimeUs === null ? [] : [{
|
|
307
|
+
coordinateSpace: "output-pixels" as const,
|
|
308
|
+
displayId: sample.displayId,
|
|
309
|
+
outputTimeUs,
|
|
310
|
+
position: globalPointToOutput(sample.position, transform),
|
|
311
|
+
sourceTimeUs: sample.sourceTimeUs,
|
|
312
|
+
visible: sample.visible,
|
|
313
|
+
}];
|
|
314
|
+
})
|
|
315
|
+
: [];
|
|
316
|
+
|
|
317
|
+
const typingSpans: RenderTypingSpan[] = [];
|
|
318
|
+
if (plan.effects.typedText.enabled) {
|
|
319
|
+
for (const span of deriveTypingSpans(events, plan.effects.typedText.idleTimeoutUs)) {
|
|
320
|
+
for (const { source, output } of mapSourceInterval(timeMap, { startUs: span.startUs, endUs: span.endUs })) {
|
|
321
|
+
const spanWindow = findWindowAtTime(events, { kind: "window-id", windowId: span.windowId }, span.startUs);
|
|
322
|
+
if (spanWindow !== null && spanWindow.displayId !== display.displayId) continue;
|
|
323
|
+
const renderedBounds = tryGlobalRectToOutput(span.bounds, transform);
|
|
324
|
+
if (renderedBounds === null) continue;
|
|
325
|
+
if (span.secure) {
|
|
326
|
+
typingSpans.push({
|
|
327
|
+
bounds: renderedBounds,
|
|
328
|
+
endOutputUs: output.endUs,
|
|
329
|
+
endSourceUs: source.endUs,
|
|
330
|
+
fieldId: "[REDACTED]",
|
|
331
|
+
secure: true,
|
|
332
|
+
startOutputUs: output.startUs,
|
|
333
|
+
startSourceUs: source.startUs,
|
|
334
|
+
state: "hidden",
|
|
335
|
+
windowId: span.windowId,
|
|
336
|
+
});
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const updates = span.updates.flatMap((update) => {
|
|
340
|
+
if (update.sourceTimeUs < source.startUs || update.sourceTimeUs >= source.endUs) return [];
|
|
341
|
+
const outputTimeUs = sourceToOutputUs(timeMap, update.sourceTimeUs);
|
|
342
|
+
const bounds = tryGlobalRectToOutput(update.bounds, transform);
|
|
343
|
+
return outputTimeUs === null || bounds === null ? [] : [{
|
|
344
|
+
...update,
|
|
345
|
+
bounds,
|
|
346
|
+
coordinateSpace: "output-pixels" as const,
|
|
347
|
+
outputTimeUs,
|
|
348
|
+
}];
|
|
349
|
+
});
|
|
350
|
+
if (updates.length > 0) {
|
|
351
|
+
typingSpans.push({
|
|
352
|
+
endOutputUs: output.endUs,
|
|
353
|
+
endSourceUs: source.endUs,
|
|
354
|
+
fieldId: span.fieldId,
|
|
355
|
+
secure: false,
|
|
356
|
+
startOutputUs: output.startUs,
|
|
357
|
+
startSourceUs: source.startUs,
|
|
358
|
+
updates,
|
|
359
|
+
windowId: span.windowId,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const clickCues = plan.effects.clicks.enabled
|
|
367
|
+
? events.flatMap((event) => {
|
|
368
|
+
if (event.type !== "mouse.click" || event.displayId !== display.displayId) return [];
|
|
369
|
+
const outputTimeUs = sourceToOutputUs(timeMap, event.sourceTimeUs);
|
|
370
|
+
return outputTimeUs === null ? [] : [{
|
|
371
|
+
button: event.button,
|
|
372
|
+
clickCount: event.clickCount,
|
|
373
|
+
coordinateSpace: "output-pixels" as const,
|
|
374
|
+
displayId: event.displayId,
|
|
375
|
+
outputTimeUs,
|
|
376
|
+
phase: event.phase,
|
|
377
|
+
position: globalPointToOutput(event.position, transform),
|
|
378
|
+
sourceTimeUs: event.sourceTimeUs,
|
|
379
|
+
}];
|
|
380
|
+
})
|
|
381
|
+
: [];
|
|
382
|
+
|
|
383
|
+
const keystrokeCues = plan.effects.keystrokes.enabled
|
|
384
|
+
? events.flatMap((event) => {
|
|
385
|
+
if (event.type !== "key.activity") return [];
|
|
386
|
+
const outputTimeUs = sourceToOutputUs(timeMap, event.sourceTimeUs);
|
|
387
|
+
if (outputTimeUs === null) return [];
|
|
388
|
+
const activity = event.activity.kind === "printable"
|
|
389
|
+
? { kind: "printable" as const, token: "[PRINTABLE]" as const }
|
|
390
|
+
: event.activity.kind === "shortcut"
|
|
391
|
+
? { keyCode: event.activity.keyCode, kind: "shortcut" as const, modifiers: event.activity.modifiers }
|
|
392
|
+
: { control: event.activity.control, kind: "control" as const, modifiers: event.activity.modifiers };
|
|
393
|
+
return [{
|
|
394
|
+
activity,
|
|
395
|
+
outputTimeUs,
|
|
396
|
+
phase: event.activity.phase,
|
|
397
|
+
repeat: event.activity.repeat,
|
|
398
|
+
sourceTimeUs: event.sourceTimeUs,
|
|
399
|
+
}];
|
|
400
|
+
})
|
|
401
|
+
: [];
|
|
402
|
+
|
|
403
|
+
return RenderPlanV1Schema.parse({
|
|
404
|
+
cameraKeyframes,
|
|
405
|
+
composition: {
|
|
406
|
+
audioTrackIds: [...options.audioTrackIds],
|
|
407
|
+
baseDisplay: { displayId: display.displayId, trackId: displayTrack.trackId },
|
|
408
|
+
camera,
|
|
409
|
+
globalToOutput: {
|
|
410
|
+
displayBoundsPoints: display.bounds,
|
|
411
|
+
displayScaleFactor: display.scaleFactor,
|
|
412
|
+
outputScaleX: transform.outputScaleX,
|
|
413
|
+
outputScaleY: transform.outputScaleY,
|
|
414
|
+
sourceSpace: "global-display-points",
|
|
415
|
+
targetSpace: "output-pixels",
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
effects: {
|
|
419
|
+
clickCues,
|
|
420
|
+
clicks: plan.effects.clicks,
|
|
421
|
+
cursor: plan.effects.cursor,
|
|
422
|
+
cursorSamples,
|
|
423
|
+
keystrokes: plan.effects.keystrokes,
|
|
424
|
+
keystrokeCues,
|
|
425
|
+
typedText: plan.effects.typedText,
|
|
426
|
+
typingSpans,
|
|
427
|
+
},
|
|
428
|
+
kind: "slopcamera.render-plan",
|
|
429
|
+
output: {
|
|
430
|
+
durationUs: timeMap.durationUs,
|
|
431
|
+
frameRate: options.frameRate,
|
|
432
|
+
pixelHeight: options.pixelHeight,
|
|
433
|
+
pixelWidth: options.pixelWidth,
|
|
434
|
+
},
|
|
435
|
+
overlays,
|
|
436
|
+
planSha256: hashEditPlan(plan),
|
|
437
|
+
recordingId: manifest.recordingId,
|
|
438
|
+
schemaVersion: 1,
|
|
439
|
+
sourceSegments,
|
|
440
|
+
timeMap: timeMap.segments,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { canonicalJsonSha256 } from "./canonical-json";
|
|
2
|
+
|
|
3
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/u;
|
|
4
|
+
const ERROR_CODE_PATTERN = /^[a-z0-9][a-z0-9-]{0,127}$/u;
|
|
5
|
+
const SCENE_ID_PATTERN = /^scene_[a-z0-9][a-z0-9_-]{7,63}$/u;
|
|
6
|
+
const SAMPLE_ID_PATTERN = /^sample_[a-z0-9][a-z0-9_-]{7,63}$/u;
|
|
7
|
+
|
|
8
|
+
export interface SceneBatchCacheIdentity {
|
|
9
|
+
readonly inputDigest: string;
|
|
10
|
+
readonly model: {
|
|
11
|
+
readonly aiSdkVersion: string;
|
|
12
|
+
readonly gateway: "vercel-ai-gateway";
|
|
13
|
+
readonly promptSha256: string;
|
|
14
|
+
readonly promptVersion: string;
|
|
15
|
+
readonly requestedModel: string;
|
|
16
|
+
readonly samplingVersion: string;
|
|
17
|
+
};
|
|
18
|
+
readonly samples: readonly {
|
|
19
|
+
readonly actualAssetTimeUs: number;
|
|
20
|
+
readonly sampleId: string;
|
|
21
|
+
readonly sha256: string;
|
|
22
|
+
}[];
|
|
23
|
+
readonly sceneIds: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function requireSha256(value: string, label: string): void {
|
|
27
|
+
if (!SHA256_PATTERN.test(value)) throw new TypeError(`${label} must be a lowercase SHA-256 digest.`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function containsControlCharacter(value: string): boolean {
|
|
31
|
+
return [...value].some((character) => {
|
|
32
|
+
const codePoint = character.codePointAt(0);
|
|
33
|
+
return codePoint !== undefined && (codePoint <= 31 || codePoint === 127);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function requireBoundedText(value: string, label: string, maximum: number): void {
|
|
38
|
+
if (value.length < 1 || value.length > maximum || containsControlCharacter(value)) {
|
|
39
|
+
throw new TypeError(`${label} must be bounded printable text.`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function sceneBatchCacheKey(identity: SceneBatchCacheIdentity): string {
|
|
44
|
+
requireSha256(identity.inputDigest, "Cache inputDigest");
|
|
45
|
+
requireSha256(identity.model.promptSha256, "Cache promptSha256");
|
|
46
|
+
requireBoundedText(identity.model.aiSdkVersion, "Cache aiSdkVersion", 128);
|
|
47
|
+
requireBoundedText(identity.model.promptVersion, "Cache promptVersion", 128);
|
|
48
|
+
requireBoundedText(identity.model.requestedModel, "Cache requestedModel", 256);
|
|
49
|
+
requireBoundedText(identity.model.samplingVersion, "Cache samplingVersion", 128);
|
|
50
|
+
if (identity.sceneIds.length < 1 || identity.sceneIds.length > 4) {
|
|
51
|
+
throw new TypeError("A scene cache batch must contain between one and four scenes.");
|
|
52
|
+
}
|
|
53
|
+
if (new Set(identity.sceneIds).size !== identity.sceneIds.length || identity.sceneIds.some(id => !SCENE_ID_PATTERN.test(id))) {
|
|
54
|
+
throw new TypeError("Cache scene IDs must be unique opaque scene identifiers.");
|
|
55
|
+
}
|
|
56
|
+
if (identity.samples.length < 1 || identity.samples.length > 12) {
|
|
57
|
+
throw new TypeError("A scene cache batch must contain between one and twelve samples.");
|
|
58
|
+
}
|
|
59
|
+
if (new Set(identity.samples.map(sample => sample.sampleId)).size !== identity.samples.length) {
|
|
60
|
+
throw new TypeError("Cache sample IDs must be unique.");
|
|
61
|
+
}
|
|
62
|
+
const samples = [...identity.samples].sort((left, right) => left.sampleId.localeCompare(right.sampleId));
|
|
63
|
+
for (const sample of samples) {
|
|
64
|
+
if (!SAMPLE_ID_PATTERN.test(sample.sampleId)) throw new TypeError("Cache sample IDs must be opaque sample identifiers.");
|
|
65
|
+
if (!Number.isSafeInteger(sample.actualAssetTimeUs) || sample.actualAssetTimeUs < 0) {
|
|
66
|
+
throw new TypeError("Cache sample times must be nonnegative safe integers.");
|
|
67
|
+
}
|
|
68
|
+
requireSha256(sample.sha256, "Cache sample sha256");
|
|
69
|
+
}
|
|
70
|
+
return canonicalJsonSha256({
|
|
71
|
+
inputDigest: identity.inputDigest,
|
|
72
|
+
model: identity.model,
|
|
73
|
+
samples,
|
|
74
|
+
sceneIds: identity.sceneIds,
|
|
75
|
+
version: 1,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type SceneBatchState = "ambiguous" | "complete" | "dispatching" | "failed" | "planned";
|
|
80
|
+
|
|
81
|
+
export interface SceneBatchStateRecord {
|
|
82
|
+
readonly batchKey: string;
|
|
83
|
+
readonly errorCode: string | null;
|
|
84
|
+
readonly imageBytes: number;
|
|
85
|
+
readonly imageCount: number;
|
|
86
|
+
readonly sceneIds: readonly string[];
|
|
87
|
+
readonly state: SceneBatchState;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type SceneBatchEvent =
|
|
91
|
+
| { readonly kind: "dispatch" }
|
|
92
|
+
| { readonly kind: "complete" }
|
|
93
|
+
| {
|
|
94
|
+
readonly errorCode: string;
|
|
95
|
+
readonly kind: "fail";
|
|
96
|
+
readonly outcome: "ambiguous" | "definitive";
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
readonly acknowledgement: "explicit-user-retry";
|
|
100
|
+
readonly kind: "retry";
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function requireStateRecord(batch: SceneBatchStateRecord): void {
|
|
104
|
+
requireSha256(batch.batchKey, "Scene batch key");
|
|
105
|
+
if (!Number.isSafeInteger(batch.imageBytes) || batch.imageBytes < 0 || batch.imageBytes > 6_000_000) {
|
|
106
|
+
throw new TypeError("Scene batch imageBytes is outside its contract.");
|
|
107
|
+
}
|
|
108
|
+
if (!Number.isSafeInteger(batch.imageCount) || batch.imageCount < 1 || batch.imageCount > 12) {
|
|
109
|
+
throw new TypeError("Scene batch imageCount is outside its contract.");
|
|
110
|
+
}
|
|
111
|
+
if (batch.sceneIds.length < 1 || batch.sceneIds.length > 4 || batch.sceneIds.some(id => !SCENE_ID_PATTERN.test(id))) {
|
|
112
|
+
throw new TypeError("Scene batch sceneIds is outside its contract.");
|
|
113
|
+
}
|
|
114
|
+
const errorExpected = batch.state === "ambiguous" || batch.state === "failed";
|
|
115
|
+
if (errorExpected !== (batch.errorCode !== null)) {
|
|
116
|
+
throw new TypeError("Scene batch failure states and error codes are inconsistent.");
|
|
117
|
+
}
|
|
118
|
+
if (batch.errorCode !== null && !ERROR_CODE_PATTERN.test(batch.errorCode)) {
|
|
119
|
+
throw new TypeError("Scene batch errorCode must be a bounded machine-readable code.");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function illegalTransition(batch: SceneBatchStateRecord, event: SceneBatchEvent): never {
|
|
124
|
+
throw new TypeError(`Scene batch cannot transition from ${batch.state} with ${event.kind}.`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function transitionSceneBatch(
|
|
128
|
+
batch: SceneBatchStateRecord,
|
|
129
|
+
event: SceneBatchEvent,
|
|
130
|
+
): SceneBatchStateRecord {
|
|
131
|
+
requireStateRecord(batch);
|
|
132
|
+
if (event.kind === "dispatch") {
|
|
133
|
+
if (batch.state !== "planned") return illegalTransition(batch, event);
|
|
134
|
+
return { ...batch, errorCode: null, state: "dispatching" };
|
|
135
|
+
}
|
|
136
|
+
if (event.kind === "complete") {
|
|
137
|
+
if (batch.state !== "dispatching") return illegalTransition(batch, event);
|
|
138
|
+
return { ...batch, errorCode: null, state: "complete" };
|
|
139
|
+
}
|
|
140
|
+
if (event.kind === "fail") {
|
|
141
|
+
if (batch.state !== "dispatching") return illegalTransition(batch, event);
|
|
142
|
+
if (!ERROR_CODE_PATTERN.test(event.errorCode)) {
|
|
143
|
+
throw new TypeError("Scene batch errorCode must be a bounded machine-readable code.");
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
...batch,
|
|
147
|
+
errorCode: event.errorCode,
|
|
148
|
+
state: event.outcome === "ambiguous" ? "ambiguous" : "failed",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (event.acknowledgement !== "explicit-user-retry") {
|
|
152
|
+
throw new TypeError("Retrying a scene batch requires explicit acknowledgement.");
|
|
153
|
+
}
|
|
154
|
+
if (batch.state !== "failed" && batch.state !== "ambiguous") return illegalTransition(batch, event);
|
|
155
|
+
return { ...batch, errorCode: null, state: "planned" };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface CompletedSceneBatchCacheEntry {
|
|
159
|
+
readonly batchKey: string;
|
|
160
|
+
readonly payloadSha256: string;
|
|
161
|
+
readonly schemaVersion: 1;
|
|
162
|
+
readonly state: "complete";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function reusableSceneBatchCacheEntry(
|
|
166
|
+
expectedBatchKey: string,
|
|
167
|
+
candidate: CompletedSceneBatchCacheEntry | null,
|
|
168
|
+
): CompletedSceneBatchCacheEntry | null {
|
|
169
|
+
requireSha256(expectedBatchKey, "Expected scene batch key");
|
|
170
|
+
if (candidate === null || candidate.schemaVersion !== 1 || candidate.state !== "complete") return null;
|
|
171
|
+
requireSha256(candidate.batchKey, "Cached scene batch key");
|
|
172
|
+
requireSha256(candidate.payloadSha256, "Cached scene batch payloadSha256");
|
|
173
|
+
return candidate.batchKey === expectedBatchKey ? candidate : null;
|
|
174
|
+
}
|