@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
package/src/config.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises"
|
|
2
|
+
import { dirname, extname, isAbsolute, resolve } from "node:path"
|
|
3
|
+
import { pathToFileURL } from "node:url"
|
|
4
|
+
import { pathExists } from "./fs.js"
|
|
5
|
+
import { builtInIcons, sanitizeIcon } from "./icons.js"
|
|
6
|
+
import type {
|
|
7
|
+
DiagramConfig,
|
|
8
|
+
FontConfig,
|
|
9
|
+
IconDefinition,
|
|
10
|
+
LoadedConfig,
|
|
11
|
+
PartialTheme,
|
|
12
|
+
} from "./types.js"
|
|
13
|
+
|
|
14
|
+
const configNames = [
|
|
15
|
+
{ current: "slopcamera.config.ts", retired: "diagram.config.ts" },
|
|
16
|
+
{ current: "slopcamera.config.mjs", retired: "diagram.config.mjs" },
|
|
17
|
+
{ current: "slopcamera.config.js", retired: "diagram.config.js" },
|
|
18
|
+
{ current: "slopcamera.config.json", retired: "diagram.config.json" },
|
|
19
|
+
] as const
|
|
20
|
+
|
|
21
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
22
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseFont(value: unknown, at: string): FontConfig {
|
|
26
|
+
if (!isRecord(value) || typeof value.family !== "string" || value.family.trim() === "") {
|
|
27
|
+
throw new Error(`${at} must have a non-empty family`)
|
|
28
|
+
}
|
|
29
|
+
if (value.files !== undefined && !Array.isArray(value.files)) {
|
|
30
|
+
throw new Error(`${at}.files must be an array`)
|
|
31
|
+
}
|
|
32
|
+
if (
|
|
33
|
+
value.monoFamily !== undefined &&
|
|
34
|
+
(typeof value.monoFamily !== "string" || value.monoFamily.trim() === "")
|
|
35
|
+
) {
|
|
36
|
+
throw new Error(`${at}.monoFamily must be a non-empty string when present`)
|
|
37
|
+
}
|
|
38
|
+
const files = (value.files ?? []).map((file, index) => {
|
|
39
|
+
if (!isRecord(file) || typeof file.path !== "string" || file.path.trim() === "") {
|
|
40
|
+
throw new Error(`${at}.files[${index}].path must be a non-empty string`)
|
|
41
|
+
}
|
|
42
|
+
if (file.weight !== undefined && (typeof file.weight !== "number" || !Number.isFinite(file.weight))) {
|
|
43
|
+
throw new Error(`${at}.files[${index}].weight must be a finite number`)
|
|
44
|
+
}
|
|
45
|
+
if (file.style !== undefined && file.style !== "normal" && file.style !== "italic") {
|
|
46
|
+
throw new Error(`${at}.files[${index}].style must be normal or italic`)
|
|
47
|
+
}
|
|
48
|
+
if (file.embed !== undefined && typeof file.embed !== "boolean") {
|
|
49
|
+
throw new Error(`${at}.files[${index}].embed must be a boolean`)
|
|
50
|
+
}
|
|
51
|
+
const style = file.style as "normal" | "italic" | undefined
|
|
52
|
+
return {
|
|
53
|
+
path: file.path,
|
|
54
|
+
...(file.weight === undefined ? {} : { weight: file.weight }),
|
|
55
|
+
...(style === undefined ? {} : { style }),
|
|
56
|
+
...(file.embed === undefined ? {} : { embed: file.embed }),
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
return {
|
|
60
|
+
family: value.family,
|
|
61
|
+
...(value.monoFamily === undefined ? {} : { monoFamily: value.monoFamily }),
|
|
62
|
+
...(files.length === 0 ? {} : { files }),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function parseIcons(value: unknown, at: string): Readonly<Record<string, IconDefinition>> {
|
|
67
|
+
if (!isRecord(value)) throw new Error(`${at} must be an object`)
|
|
68
|
+
return Object.fromEntries(
|
|
69
|
+
Object.entries(value).map(([name, icon]) => {
|
|
70
|
+
if (!isRecord(icon) || typeof icon.viewBox !== "string" || typeof icon.body !== "string") {
|
|
71
|
+
throw new Error(`${at}.${name} must have string viewBox and body fields`)
|
|
72
|
+
}
|
|
73
|
+
return [name, sanitizeIcon({ viewBox: icon.viewBox, body: icon.body })]
|
|
74
|
+
}),
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function parseTheme(value: unknown, at: string): PartialTheme {
|
|
79
|
+
if (!isRecord(value)) throw new Error(`${at} must be an object`)
|
|
80
|
+
const scalarKeys = ["background", "foreground", "muted", "stroke"] as const
|
|
81
|
+
for (const key of scalarKeys) {
|
|
82
|
+
if (value[key] !== undefined && typeof value[key] !== "string") {
|
|
83
|
+
throw new Error(`${at}.${key} must be a CSS color string`)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (value.tones !== undefined && !isRecord(value.tones)) {
|
|
87
|
+
throw new Error(`${at}.tones must be an object`)
|
|
88
|
+
}
|
|
89
|
+
return value as PartialTheme
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseConfig(value: unknown): DiagramConfig {
|
|
93
|
+
if (!isRecord(value)) throw new Error("Slopcamera config must export an object")
|
|
94
|
+
const font = value.font === undefined ? undefined : parseFont(value.font, "font")
|
|
95
|
+
const icons = value.icons === undefined ? undefined : parseIcons(value.icons, "icons")
|
|
96
|
+
let theme: DiagramConfig["theme"]
|
|
97
|
+
if (value.theme !== undefined) {
|
|
98
|
+
if (!isRecord(value.theme)) throw new Error("theme must be an object")
|
|
99
|
+
theme = {
|
|
100
|
+
...(value.theme.light === undefined ? {} : { light: parseTheme(value.theme.light, "theme.light") }),
|
|
101
|
+
...(value.theme.dark === undefined ? {} : { dark: parseTheme(value.theme.dark, "theme.dark") }),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
...(font === undefined ? {} : { font }),
|
|
106
|
+
...(icons === undefined ? {} : { icons: { ...builtInIcons, ...icons } }),
|
|
107
|
+
...(theme === undefined ? {} : { theme }),
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function discoverConfig(directory: string): Promise<string | null> {
|
|
112
|
+
let current: string | null = null
|
|
113
|
+
for (const names of configNames) {
|
|
114
|
+
const candidate = resolve(directory, names.current)
|
|
115
|
+
if (await pathExists(candidate)) {
|
|
116
|
+
current = candidate
|
|
117
|
+
break
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (current !== null) return current
|
|
121
|
+
for (const names of configNames) {
|
|
122
|
+
const candidate = resolve(directory, names.retired)
|
|
123
|
+
if (await pathExists(candidate)) {
|
|
124
|
+
const replacement = resolve(directory, names.current)
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Legacy Slopcamera config found at ${candidate}. Rename it to ${replacement}; Slopcamera does not auto-load diagram.config.*.`,
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function loadDiagramConfig(options: {
|
|
134
|
+
readonly explicitPath?: string
|
|
135
|
+
readonly searchDirectory: string
|
|
136
|
+
}): Promise<LoadedConfig> {
|
|
137
|
+
const filePath =
|
|
138
|
+
options.explicitPath === undefined
|
|
139
|
+
? await discoverConfig(options.searchDirectory)
|
|
140
|
+
: resolve(options.explicitPath)
|
|
141
|
+
if (filePath === null) {
|
|
142
|
+
return {
|
|
143
|
+
filePath: null,
|
|
144
|
+
baseDirectory: options.searchDirectory,
|
|
145
|
+
value: { icons: builtInIcons },
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (!(await pathExists(filePath))) throw new Error(`Config does not exist: ${filePath}`)
|
|
149
|
+
|
|
150
|
+
const raw: unknown =
|
|
151
|
+
extname(filePath) === ".json"
|
|
152
|
+
? JSON.parse(await readFile(filePath, "utf8"))
|
|
153
|
+
: ((await import(`${pathToFileURL(filePath).href}?v=${Date.now()}`)) as { default?: unknown })
|
|
154
|
+
.default
|
|
155
|
+
const value = parseConfig(raw)
|
|
156
|
+
const baseDirectory = dirname(filePath)
|
|
157
|
+
const font =
|
|
158
|
+
value.font === undefined
|
|
159
|
+
? undefined
|
|
160
|
+
: {
|
|
161
|
+
...value.font,
|
|
162
|
+
...(value.font.files === undefined
|
|
163
|
+
? {}
|
|
164
|
+
: {
|
|
165
|
+
files: value.font.files.map((file) => ({
|
|
166
|
+
...file,
|
|
167
|
+
path: isAbsolute(file.path) ? file.path : resolve(baseDirectory, file.path),
|
|
168
|
+
})),
|
|
169
|
+
}),
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
filePath,
|
|
173
|
+
baseDirectory,
|
|
174
|
+
value: {
|
|
175
|
+
...value,
|
|
176
|
+
icons: { ...builtInIcons, ...value.icons },
|
|
177
|
+
...(font === undefined ? {} : { font }),
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
}
|
package/src/desktop.ts
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { createHash } from "node:crypto"
|
|
2
|
+
import { createReadStream, createWriteStream } from "node:fs"
|
|
3
|
+
import { chmod, mkdir, readFile, rename, rm } from "node:fs/promises"
|
|
4
|
+
import { homedir, platform as hostPlatform, arch as hostArch } from "node:os"
|
|
5
|
+
import { dirname, join, resolve } from "node:path"
|
|
6
|
+
import { Readable } from "node:stream"
|
|
7
|
+
import { pipeline } from "node:stream/promises"
|
|
8
|
+
import { pathExists } from "./fs.js"
|
|
9
|
+
import { nonGatewayChildEnvironment } from "./process-environment.js"
|
|
10
|
+
|
|
11
|
+
const releaseApi = "https://api.github.com/repos/tldraw/tldraw-offline/releases/latest"
|
|
12
|
+
export const desktopDownloadPage = "https://offline.tldraw.com"
|
|
13
|
+
|
|
14
|
+
export interface ReleaseAsset {
|
|
15
|
+
readonly name: string
|
|
16
|
+
readonly browser_download_url: string
|
|
17
|
+
readonly size: number
|
|
18
|
+
readonly digest: string | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Release {
|
|
22
|
+
readonly tag_name: string
|
|
23
|
+
readonly html_url: string
|
|
24
|
+
readonly assets: readonly ReleaseAsset[]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
28
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseRelease(value: unknown): Release {
|
|
32
|
+
if (
|
|
33
|
+
!isRecord(value) ||
|
|
34
|
+
typeof value.tag_name !== "string" ||
|
|
35
|
+
typeof value.html_url !== "string" ||
|
|
36
|
+
!Array.isArray(value.assets)
|
|
37
|
+
) {
|
|
38
|
+
throw new Error("GitHub returned an invalid tldraw Offline release")
|
|
39
|
+
}
|
|
40
|
+
const assets = value.assets.map((asset, index) => {
|
|
41
|
+
if (
|
|
42
|
+
!isRecord(asset) ||
|
|
43
|
+
typeof asset.name !== "string" ||
|
|
44
|
+
typeof asset.browser_download_url !== "string" ||
|
|
45
|
+
typeof asset.size !== "number" ||
|
|
46
|
+
(asset.digest !== null && asset.digest !== undefined && typeof asset.digest !== "string")
|
|
47
|
+
) {
|
|
48
|
+
throw new Error(`GitHub returned an invalid release asset at index ${index}`)
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
name: asset.name,
|
|
52
|
+
browser_download_url: asset.browser_download_url,
|
|
53
|
+
size: asset.size,
|
|
54
|
+
digest: asset.digest ?? null,
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
return { tag_name: value.tag_name, html_url: value.html_url, assets }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function selectDesktopAsset(
|
|
61
|
+
release: Release,
|
|
62
|
+
platform: NodeJS.Platform = hostPlatform(),
|
|
63
|
+
architecture: string = hostArch(),
|
|
64
|
+
): ReleaseAsset {
|
|
65
|
+
const expectedName =
|
|
66
|
+
platform === "darwin"
|
|
67
|
+
? "tldraw-offline-mac-universal.dmg"
|
|
68
|
+
: platform === "win32"
|
|
69
|
+
? architecture === "arm64"
|
|
70
|
+
? "tldraw-offline-win-arm64.exe"
|
|
71
|
+
: "tldraw-offline-win-x64.exe"
|
|
72
|
+
: platform === "linux"
|
|
73
|
+
? architecture === "arm64"
|
|
74
|
+
? "tldraw-offline-linux-arm64.AppImage"
|
|
75
|
+
: "tldraw-offline-linux-x86_64.AppImage"
|
|
76
|
+
: null
|
|
77
|
+
if (expectedName === null) {
|
|
78
|
+
throw new Error(`tldraw Offline has no automated installer for ${platform}/${architecture}`)
|
|
79
|
+
}
|
|
80
|
+
const asset = release.assets.find((candidate) => candidate.name === expectedName)
|
|
81
|
+
if (asset === undefined) {
|
|
82
|
+
throw new Error(`The latest tldraw Offline release does not contain ${expectedName}`)
|
|
83
|
+
}
|
|
84
|
+
return asset
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function getLatestDesktopRelease(): Promise<Release> {
|
|
88
|
+
const response = await fetch(releaseApi, {
|
|
89
|
+
headers: {
|
|
90
|
+
Accept: "application/vnd.github+json",
|
|
91
|
+
"User-Agent": "hraness-slopcamera",
|
|
92
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
if (!response.ok) throw new Error(`GitHub release lookup failed with HTTP ${response.status}`)
|
|
96
|
+
return parseRelease((await response.json()) as unknown)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function sha256(filePath: string): Promise<string> {
|
|
100
|
+
const hash = createHash("sha256")
|
|
101
|
+
for await (const chunk of createReadStream(filePath)) hash.update(chunk)
|
|
102
|
+
return hash.digest("hex")
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function download(asset: ReleaseAsset, filePath: string): Promise<void> {
|
|
106
|
+
const response = await fetch(asset.browser_download_url, {
|
|
107
|
+
headers: { "User-Agent": "hraness-slopcamera" },
|
|
108
|
+
redirect: "follow",
|
|
109
|
+
})
|
|
110
|
+
if (!response.ok || response.body === null) {
|
|
111
|
+
throw new Error(`Installer download failed with HTTP ${response.status}`)
|
|
112
|
+
}
|
|
113
|
+
const temporary = `${filePath}.part-${process.pid}`
|
|
114
|
+
await mkdir(dirname(filePath), { recursive: true })
|
|
115
|
+
try {
|
|
116
|
+
await pipeline(
|
|
117
|
+
Readable.fromWeb(
|
|
118
|
+
response.body as unknown as import("node:stream/web").ReadableStream,
|
|
119
|
+
),
|
|
120
|
+
createWriteStream(temporary, { mode: 0o600 }),
|
|
121
|
+
)
|
|
122
|
+
const expected = asset.digest?.startsWith("sha256:") ? asset.digest.slice(7) : null
|
|
123
|
+
if (expected === null) {
|
|
124
|
+
throw new Error("GitHub did not publish a SHA-256 digest for this installer")
|
|
125
|
+
}
|
|
126
|
+
const actual = await sha256(temporary)
|
|
127
|
+
if (actual !== expected) {
|
|
128
|
+
throw new Error(`Installer checksum mismatch: expected ${expected}, received ${actual}`)
|
|
129
|
+
}
|
|
130
|
+
await rename(temporary, filePath)
|
|
131
|
+
} catch (error) {
|
|
132
|
+
await rm(temporary, { force: true })
|
|
133
|
+
throw error
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function spawnDetached(command: readonly string[]): void {
|
|
138
|
+
const child = Bun.spawn([...command], {
|
|
139
|
+
env: nonGatewayChildEnvironment(),
|
|
140
|
+
stdin: "ignore",
|
|
141
|
+
stdout: "ignore",
|
|
142
|
+
stderr: "ignore",
|
|
143
|
+
})
|
|
144
|
+
child.unref()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function installDesktop(options: {
|
|
148
|
+
readonly downloadOnly?: boolean
|
|
149
|
+
}): Promise<{ readonly filePath: string; readonly release: string }> {
|
|
150
|
+
const release = await getLatestDesktopRelease()
|
|
151
|
+
const asset = selectDesktopAsset(release)
|
|
152
|
+
const cacheDirectory = join(homedir(), ".cache", "slopcamera", "installers", release.tag_name)
|
|
153
|
+
const installerPath = join(cacheDirectory, asset.name)
|
|
154
|
+
let reusable = false
|
|
155
|
+
if (await pathExists(installerPath)) {
|
|
156
|
+
const expected = asset.digest?.startsWith("sha256:") ? asset.digest.slice(7) : null
|
|
157
|
+
reusable = expected !== null && (await sha256(installerPath)) === expected
|
|
158
|
+
}
|
|
159
|
+
if (!reusable) await download(asset, installerPath)
|
|
160
|
+
|
|
161
|
+
if (hostPlatform() === "linux") {
|
|
162
|
+
const installedPath = join(homedir(), ".local", "bin", "tldraw-offline")
|
|
163
|
+
await mkdir(dirname(installedPath), { recursive: true })
|
|
164
|
+
const temporary = `${installedPath}.tmp-${process.pid}`
|
|
165
|
+
await Bun.write(temporary, Bun.file(installerPath))
|
|
166
|
+
await chmod(temporary, 0o755)
|
|
167
|
+
await rename(temporary, installedPath)
|
|
168
|
+
if (!options.downloadOnly) spawnDetached([installedPath])
|
|
169
|
+
return { filePath: installedPath, release: release.tag_name }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!options.downloadOnly) {
|
|
173
|
+
if (hostPlatform() === "darwin") {
|
|
174
|
+
spawnDetached(["open", installerPath])
|
|
175
|
+
} else {
|
|
176
|
+
spawnDetached(["cmd.exe", "/d", "/s", "/c", "start", "", installerPath])
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return { filePath: installerPath, release: release.tag_name }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export async function findDesktopApplication(): Promise<string | null> {
|
|
183
|
+
const candidates =
|
|
184
|
+
hostPlatform() === "darwin"
|
|
185
|
+
? [
|
|
186
|
+
"/Applications/tldraw offline.app",
|
|
187
|
+
join(homedir(), "Applications", "tldraw offline.app"),
|
|
188
|
+
]
|
|
189
|
+
: hostPlatform() === "linux"
|
|
190
|
+
? [join(homedir(), ".local", "bin", "tldraw-offline")]
|
|
191
|
+
: [
|
|
192
|
+
join(
|
|
193
|
+
process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local"),
|
|
194
|
+
"Programs",
|
|
195
|
+
"tldraw offline",
|
|
196
|
+
"tldraw offline.exe",
|
|
197
|
+
),
|
|
198
|
+
]
|
|
199
|
+
for (const candidate of candidates) if (await pathExists(candidate)) return candidate
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function serverFilePath(): string {
|
|
204
|
+
if (hostPlatform() === "darwin") {
|
|
205
|
+
return join(homedir(), "Library", "Application Support", "tldraw", "server.json")
|
|
206
|
+
}
|
|
207
|
+
if (hostPlatform() === "win32") {
|
|
208
|
+
return join(process.env.APPDATA ?? join(homedir(), "AppData", "Roaming"), "tldraw", "server.json")
|
|
209
|
+
}
|
|
210
|
+
return join(process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"), "tldraw", "server.json")
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function desktopStatus(): Promise<{
|
|
214
|
+
readonly installedPath: string | null
|
|
215
|
+
readonly server: { readonly port: number; readonly pid: number | null } | null
|
|
216
|
+
}> {
|
|
217
|
+
const installedPath = await findDesktopApplication()
|
|
218
|
+
const filePath = serverFilePath()
|
|
219
|
+
if (!(await pathExists(filePath))) return { installedPath, server: null }
|
|
220
|
+
try {
|
|
221
|
+
const parsed = JSON.parse(await readFile(filePath, "utf8")) as unknown
|
|
222
|
+
if (!isRecord(parsed) || typeof parsed.port !== "number") {
|
|
223
|
+
return { installedPath, server: null }
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
installedPath,
|
|
227
|
+
server: {
|
|
228
|
+
port: parsed.port,
|
|
229
|
+
pid: typeof parsed.pid === "number" ? parsed.pid : null,
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
} catch {
|
|
233
|
+
return { installedPath, server: null }
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export async function openInDesktop(filePath: string): Promise<void> {
|
|
238
|
+
const absolutePath = resolve(filePath)
|
|
239
|
+
if (!(await pathExists(absolutePath))) throw new Error(`File does not exist: ${absolutePath}`)
|
|
240
|
+
const application = await findDesktopApplication()
|
|
241
|
+
if (application === null) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`tldraw Offline is not installed. Run "slopcamera canvas install" or visit ${desktopDownloadPage}`,
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
if (hostPlatform() === "darwin") {
|
|
247
|
+
spawnDetached(["open", "-a", application, absolutePath])
|
|
248
|
+
} else if (hostPlatform() === "win32") {
|
|
249
|
+
spawnDetached(["cmd.exe", "/d", "/s", "/c", "start", "", absolutePath])
|
|
250
|
+
} else {
|
|
251
|
+
spawnDetached([application, absolutePath])
|
|
252
|
+
}
|
|
253
|
+
}
|