@hraness/slopcamera 3.3.2 → 3.3.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.
Files changed (37) hide show
  1. package/README.md +49 -25
  2. package/apps/desktop/application/directing-contract.ts +15 -3
  3. package/apps/desktop/application/directing-plan.ts +2 -1
  4. package/apps/desktop/application/operation.ts +1 -1
  5. package/apps/desktop/cli/args.ts +1 -1
  6. package/apps/desktop/cli/cinema-renderer.ts +9 -6
  7. package/apps/desktop/cli/commands.ts +8 -1
  8. package/apps/desktop/cli/directing-blob.ts +17 -12
  9. package/apps/desktop/cli/directing-media.ts +14 -4
  10. package/apps/desktop/cli/directing-quote.ts +29 -11
  11. package/apps/desktop/cli/directing-service.ts +6 -2
  12. package/apps/desktop/cli/gateway-media-catalog.ts +57 -0
  13. package/apps/desktop/cli/gateway-media-service.ts +12 -2
  14. package/apps/desktop/cli/help.ts +4 -4
  15. package/apps/desktop/cli/main.ts +1 -1
  16. package/apps/desktop/cli/project-renderer.ts +23 -9
  17. package/apps/desktop/cli/spatial-scene-service.ts +1 -1
  18. package/apps/desktop/dist/cli/main.js +254 -248
  19. package/apps/desktop/html-overlay/spatial-splat-runtime.ts +6 -0
  20. package/dist/cli.js +1 -1
  21. package/dist/code/index.js +4 -4
  22. package/dist/{index-gs48afpt.js → index-c8jpst4g.js} +2 -2
  23. package/dist/index-z1kd1x8j.js +3 -0
  24. package/dist/index.js +1 -1
  25. package/docs/README.md +6 -3
  26. package/docs/studio.md +63 -3
  27. package/package.json +13 -7
  28. package/skills/slopcamera/references/directing-video.md +3 -3
  29. package/skills/slopcamera/references/educational-video.md +1 -1
  30. package/skills/slopcamera/references/install.md +1 -1
  31. package/skills/slopcamera/references/parametric-design.md +5 -1
  32. package/skills/slopcamera/references/scene-building.md +2 -0
  33. package/skills/slopcamera/references/workflows-sdk.md +1 -1
  34. package/src/spatial-scene/geometry.ts +88 -35
  35. package/src/spatial-scene/parametric.ts +42 -15
  36. package/src/version.ts +1 -1
  37. package/dist/index-zhg36tf9.js +0 -3
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,SLOPCAMERA_VERSION,STUDIO_LIMITS,SlopcameraCapabilityCommandSchema,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityManifestSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityWorkflowSchema,SlopcameraMcpToolRuntime,SlopcameraSceneVariantsSchema,StudioCapabilityNameSchema,StudioDigestSchema,StudioEngineOptionsSchema,StudioEngineSchema,StudioExecutionProfileSchema,StudioJobSchema,StudioOutputArtifactSchema,StudioOutputFormatSchema,StudioOutputInterpretationSchema,StudioOutputRoleSchema,StudioOutputSpecSchema,StudioPathSchema,StudioPlanSchema,StudioReceiptSchema,StudioRenderSchema,StudioRuntimeIdentitySchema,StudioSourceBundleSchema,StudioSourceSpaceSchema,WorkspaceBoundary,WorkspaceBoundaryError,artifactSummary,checkDiagramFile,compileSlopcameraCapabilityManifest,createPortableSlopcameraCapabilityManifest,defineSlopcameraCapabilityModule,diagramApi,inspectStudioBundle,inspectStudioPlan,mcpMaximumRenderedPixels,mcpMaximumScale,mcpSourceByteLimit,parseSlopcameraCapabilityManifest,parseSlopcameraSceneVariants,parseStudioJob,parseStudioPlan,parseStudioReceipt,parseStudioRuntimeIdentity,parseStudioSourceBundle,planSlopcameraSceneGallery,planStudioJob,readDiagramFile,renderDiagramFile,runMcpServer,slopcameraApi,slopcameraMcpProtocolVersion,slopcameraMcpServerName,slopcameraMcpTools,slopcameraPortableCapabilityManifest,slopcameraSceneGalleryLimits,studioJobSha256,studioOutputPath,studioRuntimeSha256,studioSourceBundleSha256,summarizeSceneVariantPatch,validateStudioReceipt}from"./index-gs48afpt.js";import{bundledSkillPath,installSkill}from"./index-grk47tgs.js";import"./index-zhg36tf9.js";import"./index-3cxf2c58.js";import{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow}from"./index-296qhpf2.js";import{DiagramValidationError,SlopcameraOperationError,StackLayoutError,builtInIcons,composeSlopcameraImageGallery,critiqueIconRaster,executeSlopcameraOperation,executeSlopcameraOperationWithLease,extractIconLineArt,galleryCandidateId,galleryPromptFor,generateSlopcameraIcon,generateSlopcameraImageGallery,iconPromptFor,iconVisualGateProblems,isSlopcameraOperationCode,lintDiagram,parseDiagramSource,parseDiagramSpec,parseSlopcameraGalleryVary,parseSlopcameraOperationInput,planSlopcameraGallery,renderPng,renderSvg,resolveDiagramSource,resolveEdge,resolveStackLayout,searchSlopcameraOperations,serializeTldr,slopcameraGalleryAxes,slopcameraGalleryKinds,slopcameraGalleryLimits,slopcameraIconCritiqueDefaultModel,slopcameraIconCritiqueTimeoutMs,slopcameraIconDefaultInk,slopcameraIconDefaultRounds,slopcameraIconMaximumRounds,slopcameraIconPanel,slopcameraIconSubjectMaximumBytes,slopcameraOperationCodes,slopcameraOperationHostResourceClaims,slopcameraOperationRegistry,stackLayoutDefaults,withSlopcameraOperationHostAdmission}from"./index-0bbesn4m.js";import{VTRACER_VERSION,VectorizeError,vectorizeDefaultLimits,vectorizeHardLimits,vectorizeImage,vectorizeProfileNames,vtracerReleases}from"./index-qry58nj2.js";import{SlopcameraCloudError,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile,resolveSlopcameraGatewayCredential,slopcameraGatewayApiBaseUrl,slopcameraGatewayCredentialStatus,slopcameraImageModels,slopcameraMaximumPromptBytes,slopcameraMaximumRawImageBytes,slopcameraResponseMediaTypes}from"./index-77fjfg9f.js";import{HOST_RESOURCE_MAX_WAIT_MILLISECONDS,HostResourceError,createDefaultHostResourceCoordinator,createHostResourceCoordinator,createProcessLocalHostResourceCoordinator,defaultSlopcameraHostResourceProfile,defaultSlopcameraHostResourceStateRoot,normalizeHostResourceClaims,normalizeHostResourceProfile,slopcameraHostResourceNames}from"./index-b3geqczn.js";import"./index-80a36bc0.js";export{withSlopcameraOperationHostAdmission,vtracerReleases,vectorizeProfileNames,vectorizeImage,vectorizeHardLimits,vectorizeDefaultLimits,validateStudioReceipt,summarizeSceneVariantPatch,studioSourceBundleSha256,studioRuntimeSha256,studioOutputPath,studioJobSha256,stackLayoutDefaults,slopcameraSceneGalleryLimits,slopcameraResponseMediaTypes,slopcameraPortableCapabilityManifest,slopcameraOperationRegistry,slopcameraOperationHostResourceClaims,slopcameraOperationCodes,slopcameraMcpTools,slopcameraMcpServerName,slopcameraMcpProtocolVersion,slopcameraMaximumRawImageBytes,slopcameraMaximumPromptBytes,slopcameraImageModels,slopcameraIconSubjectMaximumBytes,slopcameraIconPanel,slopcameraIconMaximumRounds,slopcameraIconDefaultRounds,slopcameraIconDefaultInk,slopcameraIconCritiqueTimeoutMs,slopcameraIconCritiqueDefaultModel,slopcameraHostResourceNames,slopcameraGatewayCredentialStatus,slopcameraGatewayApiBaseUrl,slopcameraGalleryLimits,slopcameraGalleryKinds,slopcameraGalleryAxes,slopcameraApi,serializeTldr,searchSlopcameraOperations,runSlopcameraWorkflow,runMcpServer,resolveStackLayout,resolveSlopcameraGatewayCredential,resolveEdge,resolveDiagramSource,renderSvg,renderPng,renderDiagramFile,readDiagramFile,planStudioJob,planSlopcameraSceneGallery,planSlopcameraGallery,parseStudioSourceBundle,parseStudioRuntimeIdentity,parseStudioReceipt,parseStudioPlan,parseStudioJob,parseSlopcameraSceneVariants,parseSlopcameraOperationInput,parseSlopcameraGalleryVary,parseSlopcameraCapabilityManifest,parseDiagramSpec,parseDiagramSource,normalizeHostResourceProfile,normalizeHostResourceClaims,mcpSourceByteLimit,mcpMaximumScale,mcpMaximumRenderedPixels,lintDiagram,isSlopcameraOperationCode,installSkill,inspectStudioPlan,inspectStudioBundle,iconVisualGateProblems,iconPromptFor,generateSlopcameraImageGallery,generateSlopcameraImageFile,generateSlopcameraImage,generateSlopcameraIcon,galleryPromptFor,galleryCandidateId,extractIconLineArt,executeSlopcameraOperationWithLease,executeSlopcameraOperation,diagramApi,defineSlopcameraWorkflow,defineSlopcameraCapabilityModule,defaultSlopcameraHostResourceStateRoot,defaultSlopcameraHostResourceProfile,critiqueIconRaster,createProcessLocalHostResourceCoordinator,createPortableSlopcameraCapabilityManifest,createHostResourceCoordinator,createFixedGatewayFetch,createDefaultHostResourceCoordinator,composeSlopcameraImageGallery,compileSlopcameraCapabilityManifest,checkDiagramFile,bundledSkillPath,builtInIcons,artifactSummary,WorkspaceBoundaryError,WorkspaceBoundary,VectorizeError,VTRACER_VERSION,StudioSourceSpaceSchema,StudioSourceBundleSchema,StudioRuntimeIdentitySchema,StudioRenderSchema,StudioReceiptSchema,StudioPlanSchema,StudioPathSchema,StudioOutputSpecSchema,StudioOutputRoleSchema,StudioOutputInterpretationSchema,StudioOutputFormatSchema,StudioOutputArtifactSchema,StudioJobSchema,StudioExecutionProfileSchema,StudioEngineSchema,StudioEngineOptionsSchema,StudioDigestSchema,StudioCapabilityNameSchema,StackLayoutError,SlopcameraWorkflowError,SlopcameraSceneVariantsSchema,SlopcameraOperationError,SlopcameraMcpToolRuntime,SlopcameraCloudError,SlopcameraCapabilityWorkflowSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityManifestSchema,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityCommandSchema,STUDIO_LIMITS,SLOPCAMERA_VERSION,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,HostResourceError,HOST_RESOURCE_MAX_WAIT_MILLISECONDS,DiagramValidationError};
2
+ import{SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,SLOPCAMERA_VERSION,STUDIO_LIMITS,SlopcameraCapabilityCommandSchema,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityManifestSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityWorkflowSchema,SlopcameraMcpToolRuntime,SlopcameraSceneVariantsSchema,StudioCapabilityNameSchema,StudioDigestSchema,StudioEngineOptionsSchema,StudioEngineSchema,StudioExecutionProfileSchema,StudioJobSchema,StudioOutputArtifactSchema,StudioOutputFormatSchema,StudioOutputInterpretationSchema,StudioOutputRoleSchema,StudioOutputSpecSchema,StudioPathSchema,StudioPlanSchema,StudioReceiptSchema,StudioRenderSchema,StudioRuntimeIdentitySchema,StudioSourceBundleSchema,StudioSourceSpaceSchema,WorkspaceBoundary,WorkspaceBoundaryError,artifactSummary,checkDiagramFile,compileSlopcameraCapabilityManifest,createPortableSlopcameraCapabilityManifest,defineSlopcameraCapabilityModule,diagramApi,inspectStudioBundle,inspectStudioPlan,mcpMaximumRenderedPixels,mcpMaximumScale,mcpSourceByteLimit,parseSlopcameraCapabilityManifest,parseSlopcameraSceneVariants,parseStudioJob,parseStudioPlan,parseStudioReceipt,parseStudioRuntimeIdentity,parseStudioSourceBundle,planSlopcameraSceneGallery,planStudioJob,readDiagramFile,renderDiagramFile,runMcpServer,slopcameraApi,slopcameraMcpProtocolVersion,slopcameraMcpServerName,slopcameraMcpTools,slopcameraPortableCapabilityManifest,slopcameraSceneGalleryLimits,studioJobSha256,studioOutputPath,studioRuntimeSha256,studioSourceBundleSha256,summarizeSceneVariantPatch,validateStudioReceipt}from"./index-c8jpst4g.js";import{bundledSkillPath,installSkill}from"./index-grk47tgs.js";import"./index-z1kd1x8j.js";import"./index-3cxf2c58.js";import{SlopcameraWorkflowError,defineSlopcameraWorkflow,runSlopcameraWorkflow}from"./index-296qhpf2.js";import{DiagramValidationError,SlopcameraOperationError,StackLayoutError,builtInIcons,composeSlopcameraImageGallery,critiqueIconRaster,executeSlopcameraOperation,executeSlopcameraOperationWithLease,extractIconLineArt,galleryCandidateId,galleryPromptFor,generateSlopcameraIcon,generateSlopcameraImageGallery,iconPromptFor,iconVisualGateProblems,isSlopcameraOperationCode,lintDiagram,parseDiagramSource,parseDiagramSpec,parseSlopcameraGalleryVary,parseSlopcameraOperationInput,planSlopcameraGallery,renderPng,renderSvg,resolveDiagramSource,resolveEdge,resolveStackLayout,searchSlopcameraOperations,serializeTldr,slopcameraGalleryAxes,slopcameraGalleryKinds,slopcameraGalleryLimits,slopcameraIconCritiqueDefaultModel,slopcameraIconCritiqueTimeoutMs,slopcameraIconDefaultInk,slopcameraIconDefaultRounds,slopcameraIconMaximumRounds,slopcameraIconPanel,slopcameraIconSubjectMaximumBytes,slopcameraOperationCodes,slopcameraOperationHostResourceClaims,slopcameraOperationRegistry,stackLayoutDefaults,withSlopcameraOperationHostAdmission}from"./index-0bbesn4m.js";import{VTRACER_VERSION,VectorizeError,vectorizeDefaultLimits,vectorizeHardLimits,vectorizeImage,vectorizeProfileNames,vtracerReleases}from"./index-qry58nj2.js";import{SlopcameraCloudError,createFixedGatewayFetch,generateSlopcameraImage,generateSlopcameraImageFile,resolveSlopcameraGatewayCredential,slopcameraGatewayApiBaseUrl,slopcameraGatewayCredentialStatus,slopcameraImageModels,slopcameraMaximumPromptBytes,slopcameraMaximumRawImageBytes,slopcameraResponseMediaTypes}from"./index-77fjfg9f.js";import{HOST_RESOURCE_MAX_WAIT_MILLISECONDS,HostResourceError,createDefaultHostResourceCoordinator,createHostResourceCoordinator,createProcessLocalHostResourceCoordinator,defaultSlopcameraHostResourceProfile,defaultSlopcameraHostResourceStateRoot,normalizeHostResourceClaims,normalizeHostResourceProfile,slopcameraHostResourceNames}from"./index-b3geqczn.js";import"./index-80a36bc0.js";export{withSlopcameraOperationHostAdmission,vtracerReleases,vectorizeProfileNames,vectorizeImage,vectorizeHardLimits,vectorizeDefaultLimits,validateStudioReceipt,summarizeSceneVariantPatch,studioSourceBundleSha256,studioRuntimeSha256,studioOutputPath,studioJobSha256,stackLayoutDefaults,slopcameraSceneGalleryLimits,slopcameraResponseMediaTypes,slopcameraPortableCapabilityManifest,slopcameraOperationRegistry,slopcameraOperationHostResourceClaims,slopcameraOperationCodes,slopcameraMcpTools,slopcameraMcpServerName,slopcameraMcpProtocolVersion,slopcameraMaximumRawImageBytes,slopcameraMaximumPromptBytes,slopcameraImageModels,slopcameraIconSubjectMaximumBytes,slopcameraIconPanel,slopcameraIconMaximumRounds,slopcameraIconDefaultRounds,slopcameraIconDefaultInk,slopcameraIconCritiqueTimeoutMs,slopcameraIconCritiqueDefaultModel,slopcameraHostResourceNames,slopcameraGatewayCredentialStatus,slopcameraGatewayApiBaseUrl,slopcameraGalleryLimits,slopcameraGalleryKinds,slopcameraGalleryAxes,slopcameraApi,serializeTldr,searchSlopcameraOperations,runSlopcameraWorkflow,runMcpServer,resolveStackLayout,resolveSlopcameraGatewayCredential,resolveEdge,resolveDiagramSource,renderSvg,renderPng,renderDiagramFile,readDiagramFile,planStudioJob,planSlopcameraSceneGallery,planSlopcameraGallery,parseStudioSourceBundle,parseStudioRuntimeIdentity,parseStudioReceipt,parseStudioPlan,parseStudioJob,parseSlopcameraSceneVariants,parseSlopcameraOperationInput,parseSlopcameraGalleryVary,parseSlopcameraCapabilityManifest,parseDiagramSpec,parseDiagramSource,normalizeHostResourceProfile,normalizeHostResourceClaims,mcpSourceByteLimit,mcpMaximumScale,mcpMaximumRenderedPixels,lintDiagram,isSlopcameraOperationCode,installSkill,inspectStudioPlan,inspectStudioBundle,iconVisualGateProblems,iconPromptFor,generateSlopcameraImageGallery,generateSlopcameraImageFile,generateSlopcameraImage,generateSlopcameraIcon,galleryPromptFor,galleryCandidateId,extractIconLineArt,executeSlopcameraOperationWithLease,executeSlopcameraOperation,diagramApi,defineSlopcameraWorkflow,defineSlopcameraCapabilityModule,defaultSlopcameraHostResourceStateRoot,defaultSlopcameraHostResourceProfile,critiqueIconRaster,createProcessLocalHostResourceCoordinator,createPortableSlopcameraCapabilityManifest,createHostResourceCoordinator,createFixedGatewayFetch,createDefaultHostResourceCoordinator,composeSlopcameraImageGallery,compileSlopcameraCapabilityManifest,checkDiagramFile,bundledSkillPath,builtInIcons,artifactSummary,WorkspaceBoundaryError,WorkspaceBoundary,VectorizeError,VTRACER_VERSION,StudioSourceSpaceSchema,StudioSourceBundleSchema,StudioRuntimeIdentitySchema,StudioRenderSchema,StudioReceiptSchema,StudioPlanSchema,StudioPathSchema,StudioOutputSpecSchema,StudioOutputRoleSchema,StudioOutputInterpretationSchema,StudioOutputFormatSchema,StudioOutputArtifactSchema,StudioJobSchema,StudioExecutionProfileSchema,StudioEngineSchema,StudioEngineOptionsSchema,StudioDigestSchema,StudioCapabilityNameSchema,StackLayoutError,SlopcameraWorkflowError,SlopcameraSceneVariantsSchema,SlopcameraOperationError,SlopcameraMcpToolRuntime,SlopcameraCloudError,SlopcameraCapabilityWorkflowSchema,SlopcameraCapabilityToolSchema,SlopcameraCapabilityResumeSchema,SlopcameraCapabilityResourceSchema,SlopcameraCapabilityQualificationSchema,SlopcameraCapabilityProfileSchema,SlopcameraCapabilityPreparationSchema,SlopcameraCapabilityOperationSchema,SlopcameraCapabilityModuleSchema,SlopcameraCapabilityModuleDeclarationSchema,SlopcameraCapabilityManifestSchema,SlopcameraCapabilityEffectSchema,SlopcameraCapabilityCommandSchema,STUDIO_LIMITS,SLOPCAMERA_VERSION,SLOPCAMERA_PORTABLE_CAPABILITY_VERSION,SLOPCAMERA_CAPABILITY_MANIFEST_LIMITS,HostResourceError,HOST_RESOURCE_MAX_WAIT_MILLISECONDS,DiagramValidationError};
package/docs/README.md CHANGED
@@ -2,19 +2,22 @@
2
2
 
3
3
  Slopcamera retains editable source, renders local or generated media, and assembles it into inspectable video projects. Choose a guide for the work you want to do.
4
4
 
5
- These pages describe Slopcamera from current source. [Install that source](how-to/use-current-source.md) to use the newest CLI, SDK and matching Agent Skill. The [installation guide](../README.md#install-slopcamera) also links the currently verified release archive. Historical Atet archives retain their original package and commands. The CLI also has an optional unbundled macOS menu-bar companion; the [menu-bar release contract](menubar-release.md) covers its prebuilt binary and LaunchAgent. The [capability reference](reference/capabilities.md) distinguishes them and lists local runtime requirements.
5
+ Install the [verified Slopcamera v3.3.2 release](../README.md#install-slopcamera) for the CLI, SDK, and matching Agent Skill. Each guide names its local runtimes and any later correction that requires [current source](how-to/use-current-source.md). Example helpers that import repository TypeScript also need a checkout; that requirement does not mean their CLI commands are unreleased. Historical Atet archives retain their original package and commands. The CLI also has an optional unbundled macOS menu-bar companion; the [menu-bar release contract](menubar-release.md) covers its prebuilt binary and LaunchAgent. The [capability reference](reference/capabilities.md) distinguishes them and lists local runtime requirements.
6
6
 
7
7
  ## Learn by making something
8
8
 
9
9
  - [Create and revise your first diagram](tutorials/first-diagram.md): make a two-node diagram, inspect its five exports, then change its source.
10
+ - [Create and revise your first animation](tutorials/first-animation.md): render an eight-second title, change its copy and color, and keep both sources.
10
11
  - [Render your first native film](tutorials/first-native-film.md): retain a Blender source, render a small shot, and export an ordinary Slopcamera project.
11
12
 
12
13
  ## Complete a task
13
14
 
14
15
  - [Run current-source commands](how-to/use-current-source.md): build an exact checkout of Slopcamera.
15
16
 
17
+ - [Render motion graphics from HTML](how-to/render-motion-graphics.md): choose among seven authoring profiles, render a graphic, and retain its source.
16
18
  - [Edit and deliver video](how-to/edit-video.md): import footage, align related tracks, place overlays, and check a delivery.
17
19
  - [Make a music video from an HTML scene](how-to/music-video.md): render authored visuals with a local track and retain separate sources in an editable project.
20
+ - [Convert raster images to SVG](how-to/vectorize-images.md): trace artwork locally, compare a duotone treatment, and inspect fidelity.
18
21
  - [Generate images, video, or narration](how-to/generate-media.md): discover Gateway capabilities, acknowledge selected uploads, and retain the result.
19
22
  - [Direct short generated clips](directing-video.md): budget, review takes, preserve endpoint continuity, and recover uncertain work.
20
23
  - [Author a native film](studio.md): use Blender, CadQuery, or Manim; retain caches; share assets and calibrated cameras.
@@ -24,7 +27,7 @@ These pages describe Slopcamera from current source. [Install that source](how-t
24
27
  - [Direct a cinematic world end to end](how-to/direct-cinematic-worlds.md): author an inert recipe pack and run the `cinematic-world` planning-and-review workflow.
25
28
  - [Make an educational video](how-to/educational-video.md): keep mathematical visuals, narration, and timing evidence revisable.
26
29
  - [Run or recover a workflow](how-to/run-workflows.md): use a built-in recipe or trusted Bun module and inspect its durable run.
27
- - [Configure Vercel](vercel.md) or [publish Slopcamera](publishing.md): provider and maintainer procedures.
30
+ - [Configure Vercel](vercel.md), [publish Slopcamera](publishing.md), [operate the hosted API](hosted-api.md), or [file a platform submission](platform-submission.md): provider and maintainer procedures.
28
31
 
29
32
  ## Look up a contract
30
33
 
@@ -40,4 +43,4 @@ These pages describe Slopcamera from current source. [Install that source](how-t
40
43
 
41
44
  ## Work with an agent
42
45
 
43
- The [Slopcamera Agent Skill](../skills/slopcamera/SKILL.md) routes an agent to the relevant task reference. Install the skill from the same source build as your CLI; installing a skill alone does not install the CLI or native tools. See the [installation instructions](../README.md#install-slopcamera).
46
+ The [Slopcamera Agent Skill](../skills/slopcamera/SKILL.md) routes an agent to the relevant task reference. Install the skill from the same release or source build as your CLI; installing a skill alone does not install the CLI or native tools. See the [installation instructions](../README.md#install-slopcamera).
package/docs/studio.md CHANGED
@@ -4,7 +4,37 @@ Slopcamera lets an agent retain a production scene, direct a native renderer, in
4
4
 
5
5
  Native authoring preserves the engine's control. A Blender source can use armatures, skinning, IK, shape keys, geometry nodes, materials, lights, cameras and simulation caches. A CadQuery program preserves dimensions and solid operations. A Manim scene preserves mathematical objects and timing. Portable meshes and finished videos are derivatives; retain the native source when later edits require it.
6
6
 
7
- Use the [Slopcamera source installation](how-to/use-current-source.md) for the `studio` commands described here. Start with the [one-second native film tutorial](tutorials/first-native-film.md) for a bounded CPU shot, or use [educational animation with narration](how-to/educational-video.md) for Manim.
7
+ The `studio` commands described here ship in the [verified Slopcamera release](reference/capabilities.md). Start with the [one-second native film tutorial](tutorials/first-native-film.md) for a bounded CPU shot, or use [educational animation with narration](how-to/educational-video.md) for Manim.
8
+
9
+ ## Inspect retained simulation examples
10
+
11
+ The [cloth example](https://slopcamera.com/docs/how-to/native-films#inspect-the-cloth-cache) retains 40 actual solver frames and replays them at 24 fps in a fresh process. The [fluid example](https://slopcamera.com/docs/how-to/native-films#inspect-the-fluid-cache) retains 32 actual Mantaflow frames, with both volume data and surface meshes. Their 1280×800 and1280×960 viewing frames keep the complete subjects visible. The liquid's low-resolution surface has visible facets; this short study does not establish physical accuracy.
12
+
13
+ Use the [native reproduction helpers](../examples/showcase/native/README.md) to bake, retain and replay each source. Changing physics requires a new bake. A camera-only change can reuse the exact existing cache.
14
+
15
+
16
+ ## Move focus through a shot
17
+
18
+ A fixed camera makes a focus pull easy to inspect. This three-second study keeps
19
+ the optical instrument, 44mm lens, materials and lights still while focus moves
20
+ from the front rim and screws toward the housing and side knob.
21
+
22
+ [View the focus study](https://slopcamera.com/docs/how-to/native-films#move-focus-through-a-shot).
23
+
24
+ The camera uses f/0.9, holds its near setting for frames 0–12, moves through
25
+ frames 13–58, and holds its far setting for frames 59–71. The endpoints are
26
+ different; this is a short shot, not a seamless loop. Play the film once and
27
+ compare the sharp edges at each end.
28
+
29
+ The [source recipe](https://github.com/hraness/slopcamera/tree/main/examples/showcase/native/focus-study)
30
+ keeps the focus schedule editable and provides three small smoke jobs before the
31
+ full film. Review those frames before spending time on the full 960×540 render.
32
+ The example was qualified on Blender 5.2.1 using Cycles on CPU.
33
+
34
+ ## Inspect the native character example
35
+
36
+ [Watch the rendered character](https://slopcamera.com/docs/how-to/native-films#animate-a-native-character), then inspect its [editable source](../examples/showcase/native/character). The six-second Blender film uses a continuous weighted arm mesh, an IK target and facial shape keys for a wave, two blinks and a smile. Its nine-bone native rig is distinct from the portable canonical humanoid retargeting contract. The MP4 retains rendered motion; keep the native source when revising the rig.
37
+
8
38
 
9
39
  ## Make the first shot
10
40
 
@@ -89,7 +119,7 @@ slopcamera studio assets import asset-plan.json --json
89
119
 
90
120
  A search input is `{"provider":"poly-haven","query":"football","type":"models","limit":5}`. A model selection is `{"provider":"poly-haven","assetId":"dirty_football","resolution":"1k","kind":"model","format":"gltf"}`. Save the complete JSON plan returned by the plan command before importing it. The default total download bound is 50 MiB. Include the returned source files explicitly in a studio bundle; acquisition never executes or inserts an asset into a native scene. See the [asset adapter contract](../apps/desktop/studio/assets/README.md) for texture/HDRI selections, current terms and supported dependencies.
91
121
 
92
- Use Blender's native importers for supplied GLB/glTF, OBJ/MTL, FBX and other supported formats. Retain all required textures, buffers and animation files. `examples/studio/blender/import_model.py` and the CadQuery STEP example show explicit local-input routes. Native imports have a broader feature set tha Slopcamera's deliberately bounded portable GLB parser; an imported native control rig is not automatically editable through portable spatial patches.
122
+ Use Blender's native importers for supplied GLB/glTF, OBJ/MTL, FBX and other supported formats. Retain all required textures, buffers and animation files. `examples/studio/blender/import_model.py` and the CadQuery STEP example show explicit local-input routes. Native imports have a broader feature set than Slopcamera's deliberately bounded portable GLB parser; an imported native control rig is not automatically editable through portable spatial patches.
93
123
 
94
124
  The original character example includes a deforming skeleton, blended weights, an IK target and facial shape keys. Keep control names and source parameters stable when directing later takes. Full upstream rigging APIs remain available to trusted authoring code, but arbitrary rig retargeting, production facial systems and third-party add-ons require their own setup and qualification.
95
125
 
@@ -104,7 +134,7 @@ Keep each native source alongside its portable representations. Use `.blend` for
104
134
  | Representation | Useful transfer | Boundary |
105
135
  | --- | --- | --- |
106
136
  | Native `.blend`, STEP and caches | Edit or rerender in the originating tool | Native rig controls, solids, solvers and procedural materials remain engine-specific |
107
- | Portable GLB | Share measured geometry, supported base-color materials and node TRS clips with Three | Admission rejects skins, morph targets and unsupported texture/material features; export an explicit compatible derivative |
137
+ | Portable GLB | Share measured geometry, supported base-color materials and node TRS clips with Three | The static profile rejects skins and morph targets. Slopcamera v3.3.1 also includes `slopcamera.glb-rigged-morph-skin-v1` for supported skins, morphs and STEP/LINEAR clips; sparse accessors, cubic animation and unsupported features still reject. Native control rigs and IK remain engine-specific |
108
138
  | sRGB PNG or retained RGB(A) video | Mount a diagram, avatar, generated shot or GPU graphic in either scene | Preserve pixel size, alpha, color interpretation and the exact source clock |
109
139
  | Splats | Film a captured appearance through the qualified Three/Spark profile | Appearance capture does not establish collision geometry, relighting or native mesh editability |
110
140
 
@@ -151,6 +181,32 @@ Blender review PNGs use the chosen view transform and sRGB display output. Linea
151
181
 
152
182
  Keep linear masters when grading or compositing requires them. Do not tone-map a review PNG a second time, apply a beauty transform to data passes or assume an exported GLB can reproduce arbitrary procedural materials. A splat capture describes observed appearance; it does not automatically provide editable objects, collision geometry or physically relightable materials.
153
183
 
184
+ ## Preserve color and alpha
185
+
186
+ Keep a display image and a compositing master when the next application needs
187
+ different color or transparency conventions. This original chart saves one
188
+ Blender Render Result as a 16-bit sRGB PNG with straight alpha and a half-float,
189
+ linear Rec.709 EXR with premultiplied alpha.
190
+
191
+ [View the color and alpha chart](https://slopcamera.com/docs/how-to/native-films#preserve-color-and-alpha).
192
+
193
+ Straight alpha stores the color separately from its coverage. Premultiplied alpha
194
+ stores color multiplied by coverage. The two half-transparent patches measured
195
+ alpha `0.5` in the EXR; the PNG measured `0.50000763` after 16-bit quantization.
196
+ The HDR patch retained linear EXR RGBA `[2, 0.5, 0.125, 1]`, while its display PNG
197
+ clipped the red channel to `1`.
198
+
199
+ The source includes a qualifier that reads the saved pixels, checks 26 numerical
200
+ conditions and makes dark and light composites for edge inspection. The dark
201
+ composite above is an SDR presentation image. The downloadable EXR preview uses
202
+ an explicit SDR conversion; neither preview displays the master's HDR range or
203
+ promises identical appearance between the two encodings.
204
+
205
+ Use the [source recipe](https://github.com/hraness/slopcamera/tree/main/examples/showcase/native/color-alpha-study)
206
+ to prepare a fresh job, run it with Blender 5.2.1 on CPU, and check its actual PNG
207
+ and EXR files. It keeps both masters and the native scene. The light composite is
208
+ a diagnostic for transparent edges; its pale labels have limited contrast.
209
+
154
210
  ## Educational films and generated media
155
211
 
156
212
  The Manim starter produces a 10-second portrait example with an original Luma presenter, vector geometry, Typst mathematics, gestures and a caption rail. Its 3–4–5 construction rearranges nine plus sixteen equal-area tiles into a five-by-five square. This illustrates one Pythagoras example; it does not claim a general proof.
@@ -225,3 +281,7 @@ success does not qualify Cycles/Metal, and its frames need separate visual revie
225
281
  The supervisor bounds combined logs, the job deadline and termination/pipe-drain grace. It monitors output and scratch budgets, retains failure evidence and tracks native process groups. Live scans tolerate bounded disappearance of temporary cache entries; final output, source and scratch scans remain strict. Budget-monitor and validation failures retain a safe failed host stage separately from authored-process logs, without storing foreign exception text. A machine-wide durable activity marker survives CLI death; unresolved custody blocks later native dispatch. Detached sessions from authored source are unsupported. Do not delete an unresolved marker to bypass admission; inspect and resolve the actual process ownership first.
226
282
 
227
283
  The native profile is trusted current-user execution without an OS sandbox. Private roots, clean environment variables, hashes and process supervision provide operational control and provenance. They do not confine arbitrary Python, installed plugins or native libraries. Keep unreviewed downloaded source inert until execution is explicitly authorized.
284
+
285
+ ## Canonical CAD variation
286
+
287
+ The [native film guide](https://slopcamera.com/docs/how-to/native-films#change-the-cad-dimensions) includes a real comparison of the 100 mm and 132 mm brackets. [Reproduce the retained solids and Blender presentation](../examples/showcase/native/README.md#build-a-cad-variation-and-round-trip-the-solid) with the exact source and measured STEP round-trip.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hraness/slopcamera",
3
- "version": "3.3.2",
3
+ "version": "3.3.4",
4
4
  "description": "A local visual studio for coding agents: author scenes, combine generated and recorded media, and export images, diagrams, animation, and video from retained sources.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -186,6 +186,7 @@
186
186
  "NOTICE.md",
187
187
  "src/**/*.ts",
188
188
  "!src/**/*.test.ts",
189
+ "!src/hosted/**",
189
190
  "!src/spatial-scene/test-fixture.ts",
190
191
  "src/assets/fonts/nebula-sans",
191
192
  "schema",
@@ -201,13 +202,13 @@
201
202
  "build:desktop": "bun run build:desktop:cli",
202
203
  "build:desktop:cli": "bun -e 'await (await import(\"node:fs/promises\")).rm(\"./apps/desktop/dist/cli\", { recursive: true, force: true })' && bun build --target=bun --minify --sourcemap=none --packages external --external @hraness/slopcamera/cli apps/desktop/cli/main.ts --outdir apps/desktop/dist/cli",
203
204
  "build:web": "bun run --cwd apps/web build",
204
- "typecheck": "bun run typecheck:sdk && bun run typecheck:desktop",
205
+ "typecheck": "bun run typecheck:sdk && bun run typecheck:desktop && bun run typecheck:api",
205
206
  "typecheck:sdk": "tsc --noEmit -p tsconfig.json",
206
207
  "typecheck:desktop": "tsc --noEmit -p apps/desktop/tsconfig.json",
207
- "lint": "bun run lint:sdk && bun run lint:desktop",
208
+ "lint": "bun run lint:sdk && bun run lint:desktop && bun run lint:api",
208
209
  "lint:sdk": "eslint src packages/scene scripts",
209
210
  "lint:desktop": "eslint apps/desktop/contracts apps/desktop/core apps/desktop/application apps/desktop/code apps/desktop/workflows apps/desktop/html-overlay apps/desktop/cli apps/desktop/qualification apps/desktop/analysis apps/desktop/studio",
210
- "test": "bun run test:sdk && bun run test:desktop && bun run --cwd apps/web test",
211
+ "test": "bun run test:sdk && bun run test:desktop && bun run test:api && bun run --cwd apps/web test",
211
212
  "test:sdk": "bun test ./src ./packages/scene",
212
213
  "test:desktop": "bun test ./apps/desktop/contracts ./apps/desktop/core ./apps/desktop/application ./apps/desktop/code ./apps/desktop/workflows ./apps/desktop/html-overlay ./apps/desktop/cli ./apps/desktop/analysis ./apps/desktop/studio",
213
214
  "build:desktop:analysis:macos": "bun run ./apps/desktop/analysis/build.ts",
@@ -232,15 +233,20 @@
232
233
  "test:package": "bun run ./scripts/package-smoke.ts",
233
234
  "check:sdk": "bun run typecheck:sdk && bun run lint:sdk && bun run build:sdk && bun run test:sdk && bun run check:release-workflows && bun run check:schema && bun run check:skill",
234
235
  "check:desktop": "bun run check:effect && bun run typecheck:desktop && bun run lint:desktop && bun run test:desktop && bun run build:desktop",
235
- "check:web": "bun run --cwd apps/web check",
236
+ "check:web": "bun apps/web/scripts/verify-example-sources.ts && bun run --cwd apps/web check",
236
237
  "kb:refresh": "bunx --bun github:hraness/kb#v0.15.2 refresh --root kb",
237
238
  "kb:check:lane": "bunx --bun github:hraness/kb#v0.15.2 check --root kb --no-catalog",
238
239
  "kb:check": "bunx --bun github:hraness/kb#v0.15.2 check --root kb && bunx --bun github:hraness/kb#v0.15.2 agents check --root kb --repo .",
239
240
  "kb:catalog": "bunx --bun github:hraness/kb#v0.15.2 catalog --root kb",
240
241
  "check:cost-surfaces": "bun ./scripts/check-cost-surfaces.mjs",
241
- "check": "bun run check:cost-surfaces && bun run check:standalone && bun run check:sdk && bun run check:desktop && bun run check:web && bun run check:standalone && bun run test:package",
242
+ "check": "bun run check:cost-surfaces && bun run check:standalone && bun run check:sdk && bun run check:desktop && bun run check:api && bun run check:web && bun run check:standalone && bun run test:package",
242
243
  "prepack": "bun run check",
243
- "check:effect": "bun run ./scripts/check-effect-policy.ts && bun test ./scripts/check-effect-architecture.test.ts"
244
+ "check:effect": "bun run ./scripts/check-effect-policy.ts && bun test ./scripts/check-effect-architecture.test.ts",
245
+ "typecheck:api": "tsc --noEmit -p apps/api/tsconfig.json",
246
+ "typecheck:objects": "tsc --noEmit -p apps/objects/tsconfig.json",
247
+ "lint:api": "eslint apps/api/src api apps/objects/src",
248
+ "test:api": "bun test ./apps/api/src",
249
+ "check:api": "bun run typecheck:api && bun run typecheck:objects && bun run lint:api && bun run test:api"
244
250
  },
245
251
  "dependencies": {
246
252
  "@ai-sdk/gateway-v4": "npm:@ai-sdk/gateway@^4.0.27",
@@ -3,11 +3,11 @@
3
3
  These commands require current source. Check the installed `slopcamera help direct` and [installation reference](install.md) before preparing a paid session.
4
4
 
5
5
 
6
- Use `slopcamera direct --help` for the installed command contract and read the [complete directing guide](https://github.com/hraness/slopcamera/blob/main/docs/directing-video.md) for recipe examples. Start with `slopcamera direct init recipe.json --json`, edit the stable `direct_<id>` and ordered shots, then run `slopcamera direct plan recipe.json --json`. Planning reads live Gateway capabilities, per-second pricing, and image transport requirements without a paid generation. Use [gateway-media.md](gateway-media.md) for model discovery and process-local credentials. Gateway reads `AI_GATEWAY_API_KEY` before `VERCEL_OIDC_TOKEN`.
6
+ Use `slopcamera direct --help` for the installed command contract and read the [complete directing guide](https://github.com/hraness/slopcamera/blob/main/docs/directing-video.md) for recipe examples. Start with `slopcamera direct init recipe.json --json`, edit the stable `direct_<id>` and ordered shots, then run `slopcamera direct plan recipe.json --json`. Planning reads live Gateway capabilities, per-second pricing, and image transport requirements without a paid generation. Resolution stays the catalog value; Alibaba Wan dispatch maps it with the shot's aspect ratio to the provider's explicit pixel size (`720p` at `16:9` becomes `1280x720`). Use [gateway-media.md](gateway-media.md) for model discovery and process-local credentials. Gateway reads `AI_GATEWAY_API_KEY` before `VERCEL_OIDC_TOKEN`.
7
7
 
8
- To use a rendered scene frame, pass the exact returned PNG path to `slopcamera direct anchor --input <image> --json`. Set a shot's `firstFrame` to `{kind: "image", source: <complete returned reference>}`. The import is local; generation uploads only the exact references acknowledged by `--allow-cloud-upload` on that invocation. A later shot can instead set `firstFrame` to `{kind: "shot-end", shotId: "<earlier-shot-id>"}` to use its currently accepted predecessor's final decoded frame. Optional `lastFrame` needs a first frame and live model support. Retain authored prompts for every shot and inspect motion, geometry, lighting, and identity at each join. Endpoint conditioning doesn't carry a provider neural checkpoint or complete 3D world state.
8
+ To use rendered or generated media, pass the exact returned path to `slopcamera direct anchor --input <media> --json`. It imports one PNG, JPEG, or WebP image, or one self-contained MP4/QuickTime clip, and returns the hash-bound source reference with measured facts. Set a shot's `firstFrame` to `{kind: "image", source: <complete returned reference>}` for a starting image. Alternatively, list up to eight complete returned references — images and video, ordered and distinct — in a shot's `references` array for reference-to-video, video-editing, motion-control, or extend-video operations that the live catalog confirms; `references` cannot combine with `firstFrame`/`lastFrame`. Alibaba Wan reference models bind references to prompt positions — name `character1`, `character2`, and so on in reference order. The import is local; generation uploads only the exact references acknowledged by `--allow-cloud-upload` on that invocation. A later shot can instead set `firstFrame` to `{kind: "shot-end", shotId: "<earlier-shot-id>"}` to use its currently accepted predecessor's final decoded frame. Optional `lastFrame` needs a first frame and live model support. Retain authored prompts for every shot and inspect motion, geometry, lighting, and identity at each join. Endpoint conditioning doesn't carry a provider neural checkpoint or complete 3D world state.
9
9
 
10
- For a shot whose planned `inputTransport` is `url`, configure an existing private Vercel Blob store and include `--allow-reference-hosting` alongside `--allow-cloud-upload`. H3 Max currently needs this path for image conditioning. Blob prefers `VERCEL_OIDC_TOKEN` with `BLOB_STORE_ID`, then falls back to `BLOB_READ_WRITE_TOKEN`; a Gateway API key alone cannot authorize Blob. The adapter copies at most two exact retained PNG, JPEG, or WebP images of 30 MiB each, verifies bytes and private access, then grants the model signed GET access that expires 15 minutes after issuance. It doesn't publish public URLs or retain credentials and signed URLs. GET access doesn't imply support for every provider download method. Budget for Blob storage, operations, and transfer separately from the Gateway estimate.
10
+ For a shot whose planned `inputTransport` is `url`, configure an existing private Vercel Blob store and include `--allow-reference-hosting` alongside `--allow-cloud-upload`. H3 Max currently needs this path for image conditioning; Seedance 2.5 uses it for references including video. Blob prefers `VERCEL_OIDC_TOKEN` with `BLOB_STORE_ID`, then falls back to `BLOB_READ_WRITE_TOKEN`; a Gateway API key alone cannot authorize Blob. The adapter copies at most eight exact retained objects — PNG, JPEG, or WebP images of 30 MiB each, or MP4/QuickTime video of 256 MiB each — verifies bytes and private access, then grants the model signed GET access that expires 15 minutes after issuance. It doesn't publish public URLs or retain credentials and signed URLs. GET access doesn't imply support for every provider download method. Budget for Blob storage, operations, and transfer separately from the Gateway estimate.
11
11
 
12
12
  Create the retained budget once with `slopcamera direct start recipe.json --budget-usd <authorized-USD> --json`. Generate one deliberate take with `slopcamera direct generate <direct-id> --shot <shot-id> --attempt <take-id> --allow-paid-generation --json`; include the upload and hosting flags required by its references and planned transport. Inspect the actual output, then record `slopcamera direct review <direct-id> --attempt <take-id> --decision accepted|rejected --note <review> --json`. Acceptance requires a completed video with its retained endpoint. A successful provider response can still fail local timestamp or color admission; retain those paid bytes for inspection and explicit conversion. Supported opaque 8-bit YUV with missing color tags uses the retained `untagged-8bit-yuv-assume-bt709-v1` policy: missing transfer, primaries, and matrix mean BT.709; missing range means limited range. Receipts preserve the observed tags and assumptions. Endpoint images become sRGB; conflicting tags, HDR, 10-bit formats, and video with alpha are rejected without tone mapping. Admitted RGB formats are opaque `rgb24`, `bgr24`, and `gbrp`. A rejected output and its budget reservation remain retained; another generation needs a new take ID and explicit paid invocation.
13
13
 
@@ -1,6 +1,6 @@
1
1
  # Make an educational film
2
2
 
3
- Use current-source `studio init lesson --template manim-lesson` for Manim visuals, or combine existing diagrams and spatial scenes when they better express the subject. Read [native studio](native-studio.md) for retained jobs and [video projects](video-projects.md) for composition.
3
+ Use `studio init lesson --template manim-lesson` for Manim visuals, or combine existing diagrams and spatial scenes when they better express the subject. Read [native studio](native-studio.md) for retained jobs and [video projects](video-projects.md) for composition.
4
4
 
5
5
  Preserve supplied mathematical facts, units and labels. Separate an explanatory illustration from a measured simulation or formal proof. Use tested lesson helpers for layout, axes, typography, equations and camera framing; inspect the actual rendered notation and endpoints rather than trusting syntactically valid code.
6
6
 
@@ -14,7 +14,7 @@ command -v slopcamera
14
14
 
15
15
  A restricted shell can omit package-manager paths. Check known host installation paths before declaring a tool unavailable. If Bun is genuinely absent, follow its official [installation guide](https://bun.sh/docs/installation) within the user’s authorized setup scope. Do not switch package managers or pipe an unreviewed installer into a shell.
16
16
 
17
- Slopcamera installs from its verified release archive: `bun add --global https://github.com/hraness/slopcamera/releases/download/v3.3.1/hraness-slopcamera-3.3.1.tgz`, then `slopcamera doctor --json`. Building from source is the contributor path. Historical Atet archives do not install the renamed CLI; never substitute `slopcamera` into an old archive URL.
17
+ Slopcamera installs from its verified release archive: `bun add --global https://github.com/hraness/slopcamera/releases/download/v3.3.3/hraness-slopcamera-3.3.3.tgz`, then `slopcamera doctor --json`. Building from source is the contributor path. Historical Atet archives do not install the renamed CLI; never substitute `slopcamera` into an old archive URL.
18
18
 
19
19
  Use an existing compatible source build when available. Otherwise follow the [complete source-install guide](https://github.com/hraness/slopcamera/blob/main/docs/how-to/use-current-source.md): clone into a new directory, record its exact commit, install locked dependencies without lifecycle scripts, build the SDK and source CLI, then define the shell command against that checkout. A clone or skill installation alone does not install the executable. Do not alter an existing active checkout to satisfy this path.
20
20
 
@@ -1,6 +1,6 @@
1
1
  # Build a revisable parametric design
2
2
 
3
- Use Slopcamera v3.3.1 or newer and `slopcamera scene design` for dimensions, repeated architectural parts, furniture, pavilions and façades that must regenerate from retained rules. This is a local mesh-design workflow; Rhino, Grasshopper and native engine execution are separate tools.
3
+ Use Slopcamera v3.3.1 or newer and `slopcamera scene design` for dimensions, repeated architectural parts, furniture, pavilions and façades that must regenerate from retained rules. This workflow uses Slopcamera's local mesh geometry and needs no additional modeling application.
4
4
 
5
5
  1. Discover original studies with `slopcamera scene design catalog --json`. Start with `scene design init <new-directory> --template crescent-pavilion|spiral-stair|ribbed-tower|modular-bookshelf`.
6
6
  2. Read the emitted `design.json` and inspect it with `scene design inspect`. Describe the user's intended shape in named parameters, derived scalar values, constraints and semantic geometry stages. Keep references to engineering knowledge separate from visual references. Do not infer structural validation from appearance.
@@ -12,3 +12,7 @@ Use Slopcamera v3.3.1 or newer and `slopcamera scene design` for dimensions, rep
12
12
  Place generated bundles below `artifacts/slopcamera/generated/`. Existing files conflict. An interrupted bundle without its final receipt is incomplete; inspect it and choose a fresh destination. The design compiler never executes source text. For native CAD operations or Blender/Cycles production shading, retain a native-studio source and use the explicit trusted-code workflow in [native studio](native-studio.md).
13
13
 
14
14
  Detailed contracts and checked commands: [design reference](https://github.com/hraness/slopcamera/blob/main/docs/parametric-design.md) and [design guide](https://github.com/hraness/slopcamera/blob/main/docs/how-to/parametric-design.md). The portable SDK exports the design compiler and starter catalog from `@hraness/slopcamera/code`; file publication and rendering belong to the CLI.
15
+
16
+ For an existing film, compile and decode assets before playback, retain exact bytes and receipts, and preserve the host's camera and interaction timing. A custom renderer must apply primitive and entity transforms once and preserve the SDK's linear material colors. Cache geometry but clone it before changing UVs; use world-scale texture coordinates for consistent finishes. Review actual camera paths for obstructing members, supported roof details, aperture depth, prop contacts and coplanar floor flicker. Verify repeated seeks and inspect the final encoded movie, not only hero stills. See the [film integration guide](https://github.com/hraness/slopcamera/blob/main/docs/how-to/parametric-design.md#integrate-a-design-into-an-existing-film).
17
+
18
+ For wall openings, `center[1]` is sill height from the wall base; arch height includes its semicircular crown. Use v3.3.3 or newer for corrected openings and boolean surfaces. Boolean inputs must be closed and satisfy hull convexity; conservative geometry budgets can reject opening counts below the schema maximum. Split a complex façade into wall segments. Distant wall LOD and collision proxies remain solid boxes; do not use them as aperture-accurate interaction geometry.
@@ -34,6 +34,8 @@ Prefer helpers over hand-written scene JSON. The `@hraness/slopcamera/code` expo
34
34
 
35
35
  `slopcamera scene asset admit model.glb --source-root <dir> --output manifest.json --json` validates a local GLB 2.0 against the closed profile, stores it content-addressed, derives model-space and scene-space bounds into a sibling facts manifest, and emits the mesh entity plus ready `add-asset`/`add-entity` patch operations. The source path must stay inside `--source-root`; unsupported GLB features reject rather than degrade. Apply the returned operations through `scene patch`, then feed the admission document itself to `scene audit --asset-bounds`.
36
36
 
37
+ Downloaded props use this same workflow. Require a self-contained GLB; any textures must be embedded PNG/JPEG. Loose glTF bundles, external textures, compressed meshes, and unsupported attributes or extensions reject. Admission defaults to a gray entity material. To preserve the model's materials and textures, set `geometry.materialMode: "source"` on the returned mesh in its `add-entity` operation before applying the patch, then inspect a rendered frame. Check each model's license and admission result; see the [local asset guide](https://github.com/hraness/slopcamera/blob/main/docs/spatial-scenes.md#admit-a-local-gltf-asset).
38
+
37
39
  ## Refine an existing scene
38
40
 
39
41
  Use this pass when an existing HTML, Three.js, or native scene needs better
@@ -15,7 +15,7 @@ For custom local authoring, use `slopcamera code init workflow.ts`, inspect the
15
15
 
16
16
  Discover exact local inputs with `slopcamera operations list|show`. No host accepts arbitrary new operation registration or caller-selected shell/argv. The fixed `slopcamera.studio.run` is an explicit exception for previously retained native source: its bundle/job input remains typed, while executable paths and trusted-current-user authority belong to the host invocation. The checked `examples/studio/native-workflow.ts` uses `defineWorkflow` and `StudioRunInputSchema` from the local entrypoint.
17
17
 
18
- `media.ingest` needs an existing ordinary project. There is no public generic arbitrary-file SDK project-create operation. Follow [video projects](video-projects.md) for the supported recording, native, and directing entry paths, or [music videos](music-video.md) for the current-source `html render` CLI that creates a project from an authored scene and optional soundtrack. Never synthesize their receipts or depend on private constructors.
18
+ `media.ingest` needs an existing ordinary project. There is no public generic arbitrary-file SDK project-create operation. Follow [video projects](video-projects.md) for the supported recording, native, and directing entry paths, or [music videos](music-video.md) for the `html render` CLI that creates a project from an authored scene and optional soundtrack. Never synthesize their receipts or depend on private constructors.
19
19
 
20
20
  ## Inspect, approve and resume exact work
21
21
 
@@ -44,6 +44,8 @@ export const SPATIAL_GEOMETRY_LIMITS = Object.freeze({
44
44
 
45
45
  export const SPATIAL_GEOMETRY_GRAPH_KIND = "slopcamera.spatial-geometry-graph" as const
46
46
  export const SPATIAL_GEOMETRY_PROFILE = "slopcamera.parametric-geometry-v1" as const
47
+ /** CSG algorithm identity is separate from the retained GLB/facts format. */
48
+ export const SPATIAL_GEOMETRY_BOOLEAN_COMPILER = "slopcamera.geometry-boolean-v2" as const
47
49
 
48
50
  const finite = z.number().finite().min(-SPATIAL_GEOMETRY_LIMITS.coordinate).max(SPATIAL_GEOMETRY_LIMITS.coordinate)
49
51
  const positive = z.number().finite().min(1e-9).max(SPATIAL_GEOMETRY_LIMITS.coordinate)
@@ -120,6 +122,7 @@ export interface SpatialGeometryEvaluation {
120
122
  readonly mesh: SpatialGeometryMesh
121
123
  readonly estimate: SpatialGeometryEstimate
122
124
  readonly graphSha256: string
125
+ readonly booleanCompiler?: typeof SPATIAL_GEOMETRY_BOOLEAN_COMPILER
123
126
  }
124
127
 
125
128
  function fail(message: string, path = "geometry"): never {
@@ -747,6 +750,13 @@ function partitionTriangles(input: Builder, planes: readonly Plane[]): Fragment[
747
750
  for (const plane of planes) {
748
751
  const next: Fragment[] = []
749
752
  for (const fragment of fragments) {
753
+ const signed = fragment.positions.map(point => plane.normal[0] * point[0] + plane.normal[1] * point[1] + plane.normal[2] * point[2] - plane.d)
754
+ // A one-sided or coplanar polygon is already a single cell. Clipping
755
+ // it into both inclusive half-spaces duplicates coplanar surfaces.
756
+ if (!signed.some(value => value < -PLANE_EPS) || !signed.some(value => value > PLANE_EPS)) {
757
+ next.push(fragment)
758
+ continue
759
+ }
750
760
  const inner = clipFragment(fragment, plane, true)
751
761
  const outer = clipFragment(fragment, plane, false)
752
762
  if (inner !== null) next.push(inner)
@@ -759,25 +769,59 @@ function partitionTriangles(input: Builder, planes: readonly Plane[]): Fragment[
759
769
  return result
760
770
  }
761
771
 
762
- /** Hull-solid membership: the fragment centroid must satisfy every support plane. */
763
- function fragmentInside(planes: readonly Plane[], fragment: Fragment): boolean {
772
+ function fragmentCentroid(fragment: Fragment): Vec3 {
764
773
  const count = fragment.positions.length
765
- const centroid: Vec3 = [
774
+ return [
766
775
  fragment.positions.reduce((sum, p) => sum + p[0], 0) / count,
767
776
  fragment.positions.reduce((sum, p) => sum + p[1], 0) / count,
768
777
  fragment.positions.reduce((sum, p) => sum + p[2], 0) / count,
769
778
  ]
770
- return planes.every(plane => plane.normal[0] * centroid[0] + plane.normal[1] * centroid[1] + plane.normal[2] * centroid[2] - plane.d <= PLANE_EPS)
779
+ }
780
+
781
+ function fragmentNormal(fragment: Fragment): Vec3 | null {
782
+ const origin = fragment.positions[0]!, normal = [0, 0, 0]
783
+ for (let index = 1; index + 1 < fragment.positions.length; index++) {
784
+ const a = fragment.positions[index]!, b = fragment.positions[index + 1]!
785
+ const u = [a[0] - origin[0], a[1] - origin[1], a[2] - origin[2]], v = [b[0] - origin[0], b[1] - origin[1], b[2] - origin[2]]
786
+ normal[0] = normal[0]! + u[1]! * v[2]! - u[2]! * v[1]!
787
+ normal[1] = normal[1]! + u[2]! * v[0]! - u[0]! * v[2]!
788
+ normal[2] = normal[2]! + u[0]! * v[1]! - u[1]! * v[0]!
789
+ }
790
+ const length = Math.hypot(...normal)
791
+ return length < 1e-12 ? null : [normal[0]! / length, normal[1]! / length, normal[2]! / length]
792
+ }
793
+
794
+ function planeDistance(plane: Plane, point: Vec3): number {
795
+ return plane.normal[0] * point[0] + plane.normal[1] * point[1] + plane.normal[2] * point[2] - plane.d
796
+ }
797
+
798
+ /** Symbolic infinitesimal occupancy on each side avoids scale-dependent probe distances. */
799
+ function insideHullSide(planes: readonly Plane[], point: Vec3, normal: Vec3, side: number): boolean {
800
+ return planes.every(plane => {
801
+ const signed = planeDistance(plane, point)
802
+ if (signed > PLANE_EPS) return false
803
+ if (signed < -PLANE_EPS) return true
804
+ return side * (plane.normal[0] * normal[0] + plane.normal[1] * normal[1] + plane.normal[2] * normal[2]) <= PLANE_EPS
805
+ })
806
+ }
807
+
808
+ function boundsTouch(a: Bounds, b: Bounds): boolean {
809
+ return [0, 1, 2].every(axis => a.min[axis]! <= b.max[axis]! + PLANE_EPS && b.min[axis]! <= a.max[axis]! + PLANE_EPS)
810
+ }
811
+
812
+ function builderBounds(input: Builder): Bounds {
813
+ const low: number[] = [Infinity, Infinity, Infinity], high: number[] = [-Infinity, -Infinity, -Infinity]
814
+ for (let i = 0; i < input.positions.length; i++) {
815
+ const axis = i % 3, value = input.positions[i]!
816
+ low[axis] = Math.min(low[axis]!, value); high[axis] = Math.max(high[axis]!, value)
817
+ }
818
+ return { min: low as unknown as Vec3, max: high as unknown as Vec3 }
771
819
  }
772
820
 
773
821
  function emitFragments(b: Builder, fragments: readonly Fragment[], flip: boolean): void {
774
822
  for (const fragment of fragments) {
775
- let z = 0
776
- for (let index = 0; index < fragment.positions.length; index++) {
777
- const a = fragment.positions[index]!, c = fragment.positions[(index + 1) % fragment.positions.length]!
778
- z += a[0] * c[1] - a[1] * c[0]
779
- }
780
- const unit: Vec3 = z < 0 ? [0, 0, -1] : [0, 0, 1]
823
+ const unit = fragmentNormal(fragment)
824
+ if (unit === null) continue
781
825
  const oriented: Vec3 = flip ? [-unit[0], -unit[1], -unit[2]] : unit
782
826
  const indices = fragment.positions.map((position, index) => vertex(b, position, oriented, fragment.uvs[index]!))
783
827
  for (let index = 1; index + 1 < indices.length; index++) {
@@ -801,30 +845,31 @@ function booleanMesh(input: Extract<SpatialGeometryNode, { kind: "boolean" }>, m
801
845
  fail("Boolean inputs exceed the bounded simple-input triangle budget.", `${path}.${index}`)
802
846
  }
803
847
  }
804
- const planesA = uniquePlanes(a, path)
805
- const planesC = cutters.map(cutter => uniquePlanes(cutter, path))
806
- assertHull(a, planesA, path)
807
- cutters.forEach((cutter, index) => assertHull(cutter, planesC[index]!, path))
848
+ const inputs = [a, ...cutters], hulls = inputs.map(mesh => uniquePlanes(mesh, path)), bounds = inputs.map(builderBounds)
849
+ inputs.forEach((mesh, index) => assertHull(mesh, hulls[index]!, path))
808
850
  const output = builder()
809
- if (input.operation === "difference") {
810
- const allCutterPlanes = planesC.flat()
811
- const kept = partitionTriangles(a, allCutterPlanes).filter(piece => !planesC.some(planes => fragmentInside(planes, piece)))
812
- emitFragments(output, kept, false)
813
- for (const [index, cutter] of cutters.entries()) {
814
- const caps = partitionTriangles(cutter, planesA).filter(piece =>
815
- fragmentInside(planesA, piece) && !planesC.some((other, otherIndex) => otherIndex !== index && fragmentInside(other, piece)))
816
- emitFragments(output, caps, true)
817
- }
818
- } else {
819
- const planesB = planesC[0]!, b = cutters[0]!
820
- const piecesA = partitionTriangles(a, planesB), piecesB = partitionTriangles(b, planesA)
821
- if (input.operation === "intersection") {
822
- emitFragments(output, piecesA.filter(piece => fragmentInside(planesB, piece)), false)
823
- emitFragments(output, piecesB.filter(piece => fragmentInside(planesA, piece)), false)
824
- } else {
825
- emitFragments(output, piecesA.filter(piece => !fragmentInside(planesB, piece)), false)
826
- emitFragments(output, piecesB.filter(piece => !fragmentInside(planesA, piece)), false)
851
+ for (const [owner, mesh] of inputs.entries()) {
852
+ const planes = hulls.flatMap((hull, index) => index !== owner && boundsTouch(bounds[owner]!, bounds[index]!) ? hull : [])
853
+ const outward: Fragment[] = [], reversed: Fragment[] = []
854
+ for (const fragment of partitionTriangles(mesh, planes)) {
855
+ const normal = fragmentNormal(fragment)
856
+ if (normal === null) continue
857
+ const point = fragmentCentroid(fragment)
858
+ const occupied = (side: number): boolean => {
859
+ const inside = hulls.map(hull => insideHullSide(hull, point, normal, side))
860
+ return input.operation === "difference" ? inside[0]! && !inside.slice(1).some(Boolean)
861
+ : input.operation === "intersection" ? inside.every(Boolean) : inside.some(Boolean)
862
+ }
863
+ const positive = occupied(1), negative = occupied(-1)
864
+ if (positive === negative) continue
865
+ // Coincident boundary faces belong to the earliest source hull. Their
866
+ // occupancy is shared; retaining both would double area and cause z-fighting.
867
+ const duplicate = hulls.slice(0, owner).some(hull => hull.every(plane => planeDistance(plane, point) <= PLANE_EPS)
868
+ && hull.some(plane => Math.abs(planeDistance(plane, point)) <= PLANE_EPS
869
+ && Math.abs(plane.normal[0] * normal[0] + plane.normal[1] * normal[1] + plane.normal[2] * normal[2]) > 1 - 1e-7))
870
+ if (!duplicate) (positive ? reversed : outward).push(fragment)
827
871
  }
872
+ emitFragments(output, outward, false); emitFragments(output, reversed, true)
828
873
  }
829
874
  output.uvsSet = [a, ...cutters].some(mesh => mesh.uvsSet)
830
875
  if (output.indices.length === 0) fail("Boolean result is empty; the inputs do not overlap as the operation requires.", path)
@@ -1010,8 +1055,15 @@ function estimateNode(node: SpatialGeometryNode, meshEstimates: ReadonlyMap<stri
1010
1055
  case "boolean": {
1011
1056
  const a = mesh(node.a)
1012
1057
  const cutters = node.operation === "difference" ? node.cutters.map(mesh) : [mesh(node.b)]
1013
- const triangles = a.triangles * (1 + cutters.reduce((sum, cutter) => sum + cutter.planes, 0))
1014
- + cutters.reduce((sum, cutter) => sum + cutter.triangles * (a.planes + 1), 0)
1058
+ const inputs = [a, ...cutters]
1059
+ // In one source triangle, n plane intersections form at most
1060
+ // 1+n(n+1)/2 cells. Each proper split adds at most two fan triangles,
1061
+ // hence 1+n(n+1) triangles per input triangle, including all cap faces.
1062
+ // Disjoint input bounds cannot cut each other's source triangles.
1063
+ const triangles = inputs.reduce((total, source, index) => {
1064
+ const planes = inputs.reduce((sum, other, otherIndex) => sum + (otherIndex !== index && boundsTouch(source.bounds, other.bounds) ? other.planes : 0), 0)
1065
+ return total + source.triangles * (1 + planes * (planes + 1))
1066
+ }, 0)
1015
1067
  const other = cutters[0]!
1016
1068
  const bounds = node.operation === "intersection"
1017
1069
  ? { min: [Math.max(a.bounds.min[0], other.bounds.min[0]), Math.max(a.bounds.min[1], other.bounds.min[1]), Math.max(a.bounds.min[2], other.bounds.min[2])] as unknown as Vec3,
@@ -1099,7 +1151,8 @@ export function evaluateSpatialGeometry(input: unknown): SpatialGeometryEvaluati
1099
1151
  if (mesh.vertices > estimate.vertices || mesh.triangles > estimate.triangles) {
1100
1152
  fail("Evaluated mesh exceeded its static budget estimate.", "geometry.output")
1101
1153
  }
1102
- return deepFreezeJson({ mesh, estimate, graphSha256: spatialValueSha256({ domain: "slopcamera.spatial-geometry-graph.v1", graph }) })
1154
+ return deepFreezeJson({ mesh, estimate, graphSha256: spatialValueSha256({ domain: "slopcamera.spatial-geometry-graph.v1", graph }),
1155
+ ...(graph.nodes.some(node => node.kind === "boolean") ? { booleanCompiler: SPATIAL_GEOMETRY_BOOLEAN_COMPILER } : {}) })
1103
1156
  }
1104
1157
 
1105
1158
  // ---------------------------------------------------------------------------