@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.
Files changed (441) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +60 -0
  3. package/PRIVACY.md +46 -0
  4. package/README.md +311 -0
  5. package/SECURITY.md +21 -0
  6. package/apps/desktop/README.md +966 -0
  7. package/apps/desktop/analysis/protocol.ts +223 -0
  8. package/apps/desktop/application/capability-binding.ts +594 -0
  9. package/apps/desktop/application/context.ts +97 -0
  10. package/apps/desktop/application/creative-iteration.ts +1385 -0
  11. package/apps/desktop/application/default-registry.ts +146 -0
  12. package/apps/desktop/application/directing-contract.ts +218 -0
  13. package/apps/desktop/application/directing-plan.ts +178 -0
  14. package/apps/desktop/application/errors.ts +18 -0
  15. package/apps/desktop/application/gateway-port.ts +867 -0
  16. package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
  17. package/apps/desktop/application/html-overlay-integrity.ts +337 -0
  18. package/apps/desktop/application/html-overlay-renderer.ts +45 -0
  19. package/apps/desktop/application/index.ts +13 -0
  20. package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
  21. package/apps/desktop/application/operation-effects.ts +94 -0
  22. package/apps/desktop/application/operation.ts +224 -0
  23. package/apps/desktop/application/operations/analysis/faces.ts +244 -0
  24. package/apps/desktop/application/operations/analysis/music.ts +248 -0
  25. package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
  26. package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
  27. package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
  28. package/apps/desktop/application/operations/analysis/shared.ts +202 -0
  29. package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
  30. package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
  31. package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
  32. package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
  33. package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
  34. package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
  35. package/apps/desktop/application/operations/gateway/image.ts +41 -0
  36. package/apps/desktop/application/operations/gateway/index.ts +17 -0
  37. package/apps/desktop/application/operations/gateway/shared.ts +122 -0
  38. package/apps/desktop/application/operations/gateway/speech.ts +41 -0
  39. package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
  40. package/apps/desktop/application/operations/gateway/video.ts +41 -0
  41. package/apps/desktop/application/operations/index.ts +31 -0
  42. package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
  43. package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
  44. package/apps/desktop/application/operations/iteration/select.ts +155 -0
  45. package/apps/desktop/application/operations/iteration/shared.ts +250 -0
  46. package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
  47. package/apps/desktop/application/operations/media/binding.ts +79 -0
  48. package/apps/desktop/application/operations/media/capabilities.ts +112 -0
  49. package/apps/desktop/application/operations/media/color-grade.ts +343 -0
  50. package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
  51. package/apps/desktop/application/operations/media/index.ts +8 -0
  52. package/apps/desktop/application/operations/media/ingest.ts +303 -0
  53. package/apps/desktop/application/operations/media/overlay.ts +729 -0
  54. package/apps/desktop/application/operations/media/shared.ts +845 -0
  55. package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
  56. package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
  57. package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
  58. package/apps/desktop/application/operations/project/snapshot.ts +95 -0
  59. package/apps/desktop/application/operations/recording/index.ts +5 -0
  60. package/apps/desktop/application/operations/recording/pause.ts +34 -0
  61. package/apps/desktop/application/operations/recording/resume.ts +34 -0
  62. package/apps/desktop/application/operations/recording/shared.ts +199 -0
  63. package/apps/desktop/application/operations/recording/start.ts +169 -0
  64. package/apps/desktop/application/operations/recording/stop.ts +34 -0
  65. package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
  66. package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
  67. package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
  68. package/apps/desktop/application/operations/render/project-plan.ts +939 -0
  69. package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
  70. package/apps/desktop/application/operations/render/project.ts +1922 -0
  71. package/apps/desktop/application/operations/shared.ts +67 -0
  72. package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
  73. package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
  74. package/apps/desktop/application/operations/spatial-project.ts +209 -0
  75. package/apps/desktop/application/operations/spatial-render.ts +415 -0
  76. package/apps/desktop/application/operations/spatial-scene.ts +97 -0
  77. package/apps/desktop/application/operations/studio.ts +143 -0
  78. package/apps/desktop/application/output-publication-lease.ts +163 -0
  79. package/apps/desktop/application/project-publication-lease.ts +233 -0
  80. package/apps/desktop/application/project-store.ts +196 -0
  81. package/apps/desktop/application/receipts.ts +533 -0
  82. package/apps/desktop/application/registry.ts +182 -0
  83. package/apps/desktop/application/spatial-assets.ts +675 -0
  84. package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
  85. package/apps/desktop/application/spatial-project-authority.ts +35 -0
  86. package/apps/desktop/application/spatial-project-lease.ts +126 -0
  87. package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
  88. package/apps/desktop/application/spatial-project-store.ts +286 -0
  89. package/apps/desktop/application/spatial-render-projection.ts +201 -0
  90. package/apps/desktop/application/spatial-render.ts +677 -0
  91. package/apps/desktop/application/spatial-spz.ts +44 -0
  92. package/apps/desktop/application/spatial-world-import.ts +135 -0
  93. package/apps/desktop/application/spatial-world-provenance.ts +46 -0
  94. package/apps/desktop/application/studio-port.ts +58 -0
  95. package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
  96. package/apps/desktop/capture/protocol.ts +689 -0
  97. package/apps/desktop/cli/analyzer.ts +406 -0
  98. package/apps/desktop/cli/args.ts +3181 -0
  99. package/apps/desktop/cli/asset-ingest.ts +747 -0
  100. package/apps/desktop/cli/atomic-render-effects.ts +210 -0
  101. package/apps/desktop/cli/atomic-render-platform.ts +205 -0
  102. package/apps/desktop/cli/atomic-render.ts +50 -0
  103. package/apps/desktop/cli/audio-analysis.ts +252 -0
  104. package/apps/desktop/cli/bounded-map.ts +39 -0
  105. package/apps/desktop/cli/bundle-service.ts +256 -0
  106. package/apps/desktop/cli/capabilities.ts +212 -0
  107. package/apps/desktop/cli/capture-bundle.ts +1117 -0
  108. package/apps/desktop/cli/command-host-resources.ts +251 -0
  109. package/apps/desktop/cli/commands.ts +6836 -0
  110. package/apps/desktop/cli/directing-blob.ts +291 -0
  111. package/apps/desktop/cli/directing-media.ts +517 -0
  112. package/apps/desktop/cli/directing-quote.ts +89 -0
  113. package/apps/desktop/cli/directing-service.ts +288 -0
  114. package/apps/desktop/cli/emoji-assets.ts +538 -0
  115. package/apps/desktop/cli/errors.ts +29 -0
  116. package/apps/desktop/cli/face-analysis-service.ts +940 -0
  117. package/apps/desktop/cli/filter-script.ts +76 -0
  118. package/apps/desktop/cli/gateway-application-port.ts +1142 -0
  119. package/apps/desktop/cli/gateway-credential.ts +81 -0
  120. package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
  121. package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
  122. package/apps/desktop/cli/gateway-media-service.ts +3106 -0
  123. package/apps/desktop/cli/gateway-media-signature.ts +122 -0
  124. package/apps/desktop/cli/gateway-provider-options.ts +562 -0
  125. package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
  126. package/apps/desktop/cli/help.ts +501 -0
  127. package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
  128. package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
  129. package/apps/desktop/cli/io.ts +290 -0
  130. package/apps/desktop/cli/main.ts +123 -0
  131. package/apps/desktop/cli/media-effects-service.ts +885 -0
  132. package/apps/desktop/cli/media-ingest-model.ts +364 -0
  133. package/apps/desktop/cli/media-ingest-platform.ts +227 -0
  134. package/apps/desktop/cli/media-ingest-program.ts +200 -0
  135. package/apps/desktop/cli/media-ingest.ts +30 -0
  136. package/apps/desktop/cli/music-analysis-service.ts +434 -0
  137. package/apps/desktop/cli/mutation-lock.ts +530 -0
  138. package/apps/desktop/cli/native-assets.d.ts +12 -0
  139. package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
  140. package/apps/desktop/cli/overlay-playback.ts +177 -0
  141. package/apps/desktop/cli/paths.ts +265 -0
  142. package/apps/desktop/cli/portable-surface.ts +242 -0
  143. package/apps/desktop/cli/project-camera-receipt.ts +112 -0
  144. package/apps/desktop/cli/project-face-camera.ts +394 -0
  145. package/apps/desktop/cli/project-inactivity-service.ts +552 -0
  146. package/apps/desktop/cli/project-media-integrity.ts +121 -0
  147. package/apps/desktop/cli/project-renderer.ts +1526 -0
  148. package/apps/desktop/cli/project-service.ts +519 -0
  149. package/apps/desktop/cli/project-state-transaction.ts +520 -0
  150. package/apps/desktop/cli/query-limits.ts +1 -0
  151. package/apps/desktop/cli/recording-controller.ts +1567 -0
  152. package/apps/desktop/cli/recording-daemon.ts +931 -0
  153. package/apps/desktop/cli/recording-ref.ts +56 -0
  154. package/apps/desktop/cli/renamed-environment.ts +6 -0
  155. package/apps/desktop/cli/renderer.ts +1439 -0
  156. package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
  157. package/apps/desktop/cli/scene-analysis-service.ts +879 -0
  158. package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
  159. package/apps/desktop/cli/spatial-project-service.ts +90 -0
  160. package/apps/desktop/cli/spatial-scene-service.ts +105 -0
  161. package/apps/desktop/cli/spatial-world-service.ts +39 -0
  162. package/apps/desktop/cli/speech-analysis-service.ts +682 -0
  163. package/apps/desktop/cli/studio-assemble.ts +58 -0
  164. package/apps/desktop/cli/studio-command.ts +60 -0
  165. package/apps/desktop/cli/studio-custody.ts +60 -0
  166. package/apps/desktop/cli/studio-encode.ts +216 -0
  167. package/apps/desktop/cli/studio-exr.ts +52 -0
  168. package/apps/desktop/cli/studio-files.ts +179 -0
  169. package/apps/desktop/cli/studio-output-validation.ts +162 -0
  170. package/apps/desktop/cli/studio-process.ts +145 -0
  171. package/apps/desktop/cli/studio-runtime.ts +77 -0
  172. package/apps/desktop/cli/studio-scaffold.ts +70 -0
  173. package/apps/desktop/cli/studio-service.ts +301 -0
  174. package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
  175. package/apps/desktop/cli/studio-template-names.ts +3 -0
  176. package/apps/desktop/cli/studio-workflow.ts +19 -0
  177. package/apps/desktop/cli/time.ts +25 -0
  178. package/apps/desktop/cli/workflow-code.ts +584 -0
  179. package/apps/desktop/cli/workflow-runs.ts +576 -0
  180. package/apps/desktop/code/advanced.ts +139 -0
  181. package/apps/desktop/code/application-node-planner.ts +1288 -0
  182. package/apps/desktop/code/compiler.ts +329 -0
  183. package/apps/desktop/code/contracts.ts +13 -0
  184. package/apps/desktop/code/creative-recipes.ts +94 -0
  185. package/apps/desktop/code/define-workflow.ts +115 -0
  186. package/apps/desktop/code/editing.ts +284 -0
  187. package/apps/desktop/code/file-candidate-provenance.ts +420 -0
  188. package/apps/desktop/code/file-candidate.ts +69 -0
  189. package/apps/desktop/code/graph-builder.ts +10 -0
  190. package/apps/desktop/code/host-resource-policy.ts +55 -0
  191. package/apps/desktop/code/plan-contracts.ts +159 -0
  192. package/apps/desktop/code/planning.ts +361 -0
  193. package/apps/desktop/code/public.ts +134 -0
  194. package/apps/desktop/code/reference-projection.ts +62 -0
  195. package/apps/desktop/code/run-contracts.ts +466 -0
  196. package/apps/desktop/code/run-store.ts +2478 -0
  197. package/apps/desktop/code/runtime-identity.ts +328 -0
  198. package/apps/desktop/code/scheduler.ts +3067 -0
  199. package/apps/desktop/code/semantic-builder.ts +1436 -0
  200. package/apps/desktop/code/source-bundle.ts +1641 -0
  201. package/apps/desktop/code/source-typecheck.ts +211 -0
  202. package/apps/desktop/code/testing.ts +79 -0
  203. package/apps/desktop/code/worker-client.ts +1675 -0
  204. package/apps/desktop/code/worker-effects.ts +141 -0
  205. package/apps/desktop/code/worker-entry.ts +435 -0
  206. package/apps/desktop/code/worker-lease-guardian.ts +179 -0
  207. package/apps/desktop/code/worker-process-identity.ts +484 -0
  208. package/apps/desktop/code/worker-protocol.ts +211 -0
  209. package/apps/desktop/code/workflow-effects.ts +132 -0
  210. package/apps/desktop/contracts/analysis.ts +949 -0
  211. package/apps/desktop/contracts/edit.ts +576 -0
  212. package/apps/desktop/contracts/index.ts +9 -0
  213. package/apps/desktop/contracts/media-effects.ts +139 -0
  214. package/apps/desktop/contracts/project-render.ts +644 -0
  215. package/apps/desktop/contracts/project.ts +740 -0
  216. package/apps/desktop/contracts/recording.ts +1434 -0
  217. package/apps/desktop/contracts/render.ts +334 -0
  218. package/apps/desktop/contracts/runtime.ts +155 -0
  219. package/apps/desktop/contracts/spatial-compositor.ts +58 -0
  220. package/apps/desktop/contracts/spatial-project.ts +159 -0
  221. package/apps/desktop/contracts/spatial-world.ts +48 -0
  222. package/apps/desktop/contracts/video-effects.ts +120 -0
  223. package/apps/desktop/core/alignment-apply.ts +400 -0
  224. package/apps/desktop/core/audio-alignment.ts +466 -0
  225. package/apps/desktop/core/auto-zoom.ts +209 -0
  226. package/apps/desktop/core/canonical-json.ts +14 -0
  227. package/apps/desktop/core/emoji.ts +210 -0
  228. package/apps/desktop/core/events.ts +409 -0
  229. package/apps/desktop/core/face-framing.ts +533 -0
  230. package/apps/desktop/core/face-tracks.ts +326 -0
  231. package/apps/desktop/core/inactivity.ts +137 -0
  232. package/apps/desktop/core/index.ts +30 -0
  233. package/apps/desktop/core/intervals.ts +123 -0
  234. package/apps/desktop/core/montage.ts +236 -0
  235. package/apps/desktop/core/music-analysis.ts +518 -0
  236. package/apps/desktop/core/plan.ts +244 -0
  237. package/apps/desktop/core/project-auto-zoom.ts +75 -0
  238. package/apps/desktop/core/project-camera-spatial.ts +676 -0
  239. package/apps/desktop/core/project-camera.ts +403 -0
  240. package/apps/desktop/core/project-captions.ts +888 -0
  241. package/apps/desktop/core/project-inactivity.ts +125 -0
  242. package/apps/desktop/core/project-metadata.ts +927 -0
  243. package/apps/desktop/core/project-plan.ts +375 -0
  244. package/apps/desktop/core/project-render-plan.ts +303 -0
  245. package/apps/desktop/core/project-time.ts +469 -0
  246. package/apps/desktop/core/render-plan.ts +442 -0
  247. package/apps/desktop/core/scene-cache.ts +174 -0
  248. package/apps/desktop/core/scene-sampling.ts +448 -0
  249. package/apps/desktop/core/spatial-compositor.ts +22 -0
  250. package/apps/desktop/core/spatial-durability.ts +99 -0
  251. package/apps/desktop/core/spatial-project.ts +169 -0
  252. package/apps/desktop/core/spatial-shot-clock.ts +32 -0
  253. package/apps/desktop/core/speech-analysis.ts +333 -0
  254. package/apps/desktop/core/storage.ts +970 -0
  255. package/apps/desktop/core/time-map.ts +91 -0
  256. package/apps/desktop/core/time.ts +61 -0
  257. package/apps/desktop/core/video-effects.ts +660 -0
  258. package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
  259. package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  260. package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  261. package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
  262. package/apps/desktop/dist/cli/main.js +5086 -0
  263. package/apps/desktop/html-overlay/catalog.ts +171 -0
  264. package/apps/desktop/html-overlay/contracts.ts +372 -0
  265. package/apps/desktop/html-overlay/execution-profile.ts +77 -0
  266. package/apps/desktop/html-overlay/index.ts +98 -0
  267. package/apps/desktop/html-overlay/libraries.ts +263 -0
  268. package/apps/desktop/html-overlay/random.ts +49 -0
  269. package/apps/desktop/html-overlay/runtime.ts +813 -0
  270. package/apps/desktop/html-overlay/scaffolds.ts +792 -0
  271. package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
  272. package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
  273. package/apps/desktop/html-overlay/spatial.ts +720 -0
  274. package/apps/desktop/studio/assets/README.md +39 -0
  275. package/apps/desktop/studio/assets/contracts.ts +129 -0
  276. package/apps/desktop/studio/assets/network.ts +64 -0
  277. package/apps/desktop/studio/assets/poly-haven.ts +148 -0
  278. package/apps/desktop/studio/drivers/blender_driver.py +466 -0
  279. package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
  280. package/apps/desktop/studio/education/driver.py +308 -0
  281. package/apps/desktop/studio/education/lesson.py +180 -0
  282. package/apps/desktop/studio/education/toolkit.py +249 -0
  283. package/apps/desktop/workflows/chaptered-demo.ts +48 -0
  284. package/apps/desktop/workflows/creative-iteration.ts +107 -0
  285. package/apps/desktop/workflows/creative-selection.ts +92 -0
  286. package/apps/desktop/workflows/fragments.ts +486 -0
  287. package/apps/desktop/workflows/index.ts +93 -0
  288. package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
  289. package/apps/desktop/workflows/scene-direction.ts +12 -0
  290. package/apps/desktop/workflows/social-variants.ts +75 -0
  291. package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
  292. package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
  293. package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  294. package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  295. package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
  296. package/dist/cli.js +479 -0
  297. package/dist/code/advanced.js +270 -0
  298. package/dist/code/index.js +2240 -0
  299. package/dist/generate.js +24 -0
  300. package/dist/host-resources.js +26 -0
  301. package/dist/index-42zsesc1.js +2257 -0
  302. package/dist/index-7308egqr.js +48 -0
  303. package/dist/index-8txs6fkn.js +712 -0
  304. package/dist/index-mcy8z0br.js +192 -0
  305. package/dist/index-p63wavx0.js +2029 -0
  306. package/dist/index-r7gdhmsp.js +424 -0
  307. package/dist/index-sh6xbav6.js +1219 -0
  308. package/dist/index-z1w83f81.js +4 -0
  309. package/dist/index-z7239b4h.js +2363 -0
  310. package/dist/index-zfeg9hmw.js +2013 -0
  311. package/dist/index.js +241 -0
  312. package/dist/operations.js +29 -0
  313. package/dist/skill-install-dh0ntqba.js +10 -0
  314. package/dist/vectorize/worker.js +142 -0
  315. package/dist/workflow.js +16 -0
  316. package/docs/README.md +38 -0
  317. package/docs/studio.md +215 -0
  318. package/examples/studio/blender/README.md +26 -0
  319. package/examples/studio/blender/character.py +152 -0
  320. package/examples/studio/blender/cloth.py +101 -0
  321. package/examples/studio/blender/color_chart.py +29 -0
  322. package/examples/studio/blender/fluid.py +78 -0
  323. package/examples/studio/blender/import_model.py +74 -0
  324. package/examples/studio/blender/product.py +53 -0
  325. package/examples/studio/blender/shaded_street.py +481 -0
  326. package/examples/studio/blender/studio_scene.py +147 -0
  327. package/examples/studio/cadquery/README.md +13 -0
  328. package/examples/studio/cadquery/bracket.py +36 -0
  329. package/examples/studio/cadquery/import_step.py +12 -0
  330. package/examples/studio/education/README.md +46 -0
  331. package/examples/studio/education/lesson.json +99 -0
  332. package/examples/studio/education/scene.py +7 -0
  333. package/examples/studio/hybrid-scene.ts +96 -0
  334. package/examples/studio/native-workflow.ts +14 -0
  335. package/examples/studio/vgpu/README.md +64 -0
  336. package/examples/studio/vgpu/heat-field.mjs +290 -0
  337. package/package.json +304 -0
  338. package/packages/scene/src/index.ts +1 -0
  339. package/packages/scene/src/scene.ts +215 -0
  340. package/schema/diagram.schema.json +334 -0
  341. package/skills/slopcamera/SKILL.md +45 -0
  342. package/skills/slopcamera/agents/openai.yaml +4 -0
  343. package/skills/slopcamera/references/customization.md +77 -0
  344. package/skills/slopcamera/references/diagrams.md +153 -0
  345. package/skills/slopcamera/references/directed-scenes.md +42 -0
  346. package/skills/slopcamera/references/directing-video.md +18 -0
  347. package/skills/slopcamera/references/educational-video.md +26 -0
  348. package/skills/slopcamera/references/gateway-media.md +134 -0
  349. package/skills/slopcamera/references/install.md +62 -0
  350. package/skills/slopcamera/references/native-studio.md +31 -0
  351. package/skills/slopcamera/references/reference-led-3d.md +140 -0
  352. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
  353. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
  354. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
  355. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
  356. package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
  357. package/skills/slopcamera/references/vectorization.md +29 -0
  358. package/skills/slopcamera/references/video-projects.md +187 -0
  359. package/skills/slopcamera/references/visual-communication.md +120 -0
  360. package/skills/slopcamera/references/workflows-sdk.md +45 -0
  361. package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
  362. package/src/artifacts.ts +106 -0
  363. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  364. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  365. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  366. package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  367. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  368. package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
  369. package/src/assets.d.ts +9 -0
  370. package/src/cli.ts +601 -0
  371. package/src/cloud-errors.ts +26 -0
  372. package/src/code/advanced.ts +10 -0
  373. package/src/code/boundary.ts +21 -0
  374. package/src/code/canonical-json.ts +129 -0
  375. package/src/code/compiler.ts +953 -0
  376. package/src/code/contracts.ts +576 -0
  377. package/src/code/define-workflow.ts +334 -0
  378. package/src/code/errors.ts +41 -0
  379. package/src/code/graph-builder.ts +945 -0
  380. package/src/code/index.ts +121 -0
  381. package/src/code/json-snapshot.ts +899 -0
  382. package/src/code/json-utf8.ts +88 -0
  383. package/src/code/portable-builder.ts +136 -0
  384. package/src/code/projection.ts +378 -0
  385. package/src/code/public-operations.ts +450 -0
  386. package/src/code/runtime.ts +580 -0
  387. package/src/code/sha256.ts +36 -0
  388. package/src/config.ts +180 -0
  389. package/src/desktop.ts +253 -0
  390. package/src/fs.ts +10 -0
  391. package/src/generate.ts +704 -0
  392. package/src/host-resource-posix.ts +161 -0
  393. package/src/host-resources.ts +1749 -0
  394. package/src/icons.ts +58 -0
  395. package/src/index.ts +200 -0
  396. package/src/label-layout.ts +130 -0
  397. package/src/layout.ts +413 -0
  398. package/src/lint.ts +127 -0
  399. package/src/mcp/boundary.ts +354 -0
  400. package/src/mcp/index.ts +33 -0
  401. package/src/mcp/server.ts +272 -0
  402. package/src/mcp/tools.ts +963 -0
  403. package/src/mcp/types.ts +71 -0
  404. package/src/operations.ts +775 -0
  405. package/src/parse.ts +861 -0
  406. package/src/process-environment.ts +17 -0
  407. package/src/render.ts +437 -0
  408. package/src/skill-install.ts +49 -0
  409. package/src/spatial-scene/authoring.ts +27 -0
  410. package/src/spatial-scene/camera-track.ts +83 -0
  411. package/src/spatial-scene/contracts.ts +277 -0
  412. package/src/spatial-scene/evaluate.ts +145 -0
  413. package/src/spatial-scene/gltf.ts +558 -0
  414. package/src/spatial-scene/identity.ts +260 -0
  415. package/src/spatial-scene/index.ts +10 -0
  416. package/src/spatial-scene/inspect.ts +68 -0
  417. package/src/spatial-scene/math.ts +280 -0
  418. package/src/spatial-scene/patch.ts +131 -0
  419. package/src/spatial-scene/time.ts +53 -0
  420. package/src/studio/contracts.ts +129 -0
  421. package/src/studio/index.ts +2 -0
  422. package/src/studio/plan.ts +156 -0
  423. package/src/studio/shared.ts +41 -0
  424. package/src/theme.ts +68 -0
  425. package/src/tldr.ts +474 -0
  426. package/src/types.ts +212 -0
  427. package/src/vectorize/archive.ts +101 -0
  428. package/src/vectorize/command.ts +797 -0
  429. package/src/vectorize/index.ts +27 -0
  430. package/src/vectorize/limits.ts +61 -0
  431. package/src/vectorize/metrics.ts +351 -0
  432. package/src/vectorize/pixels.ts +323 -0
  433. package/src/vectorize/supervisor.ts +339 -0
  434. package/src/vectorize/svg.ts +188 -0
  435. package/src/vectorize/tool.ts +542 -0
  436. package/src/vectorize/types.ts +113 -0
  437. package/src/vectorize/vectorize.ts +634 -0
  438. package/src/vectorize/worker-protocol.ts +40 -0
  439. package/src/vectorize/worker.ts +209 -0
  440. package/src/version.ts +1 -0
  441. package/src/workflow.ts +409 -0
@@ -0,0 +1,27 @@
1
+ export { vectorizeDefaultLimits, vectorizeHardLimits } from "./limits.js"
2
+ export {
3
+ alphaPlaneTraceRgba,
4
+ dominantOklabDuotoneModel,
5
+ lowAlphaMassRatio,
6
+ normalizedAlphaRmse,
7
+ normalizedPremultipliedRmse,
8
+ sanitizedTraceRgba,
9
+ } from "./metrics.js"
10
+ export { assertSafeCanonicalSvg, countSvgPaths } from "./svg.js"
11
+ export { VTRACER_VERSION, vtracerReleases } from "./tool.js"
12
+ export { vectorizeImage } from "./vectorize.js"
13
+ export {
14
+ VectorizeError,
15
+ vectorizeProfileNames,
16
+ type VectorizeErrorCode,
17
+ type VectorizeInput,
18
+ type VectorizeLimits,
19
+ type VectorizeOptions,
20
+ type VectorizeOutputMode,
21
+ type VectorizeProfile,
22
+ type VectorizeProvenance,
23
+ type VectorizeQualityReceipt,
24
+ type VectorizeReceipt,
25
+ type VectorizeRepresentation,
26
+ type VectorizeResult,
27
+ } from "./types.js"
@@ -0,0 +1,61 @@
1
+ import { VectorizeError, type VectorizeLimits } from "./types.js"
2
+
3
+ export const vectorizeHardLimits = Object.freeze({
4
+ maxDecodedPixels: 16_777_216,
5
+ maxDimension: 4_096,
6
+ maxDurationMs: 120_000,
7
+ maxInputBytes: 16 * 1_024 * 1_024,
8
+ maxOutputBytes: 2_000_000,
9
+ maxPaths: 12_000,
10
+ }) satisfies Readonly<VectorizeLimits>
11
+
12
+ export const vectorizeDefaultLimits = Object.freeze({
13
+ ...vectorizeHardLimits,
14
+ maxDurationMs: 30_000,
15
+ }) satisfies Readonly<VectorizeLimits>
16
+
17
+ const limitNames = Object.keys(vectorizeHardLimits) as (keyof VectorizeLimits)[]
18
+
19
+ export function resolveVectorizeLimits(
20
+ input: Partial<VectorizeLimits> | undefined,
21
+ ): Readonly<VectorizeLimits> {
22
+ const resolved: VectorizeLimits = {
23
+ maxDecodedPixels: input?.maxDecodedPixels ?? vectorizeDefaultLimits.maxDecodedPixels,
24
+ maxDimension: input?.maxDimension ?? vectorizeDefaultLimits.maxDimension,
25
+ maxDurationMs: input?.maxDurationMs ?? vectorizeDefaultLimits.maxDurationMs,
26
+ maxInputBytes: input?.maxInputBytes ?? vectorizeDefaultLimits.maxInputBytes,
27
+ maxOutputBytes: input?.maxOutputBytes ?? vectorizeDefaultLimits.maxOutputBytes,
28
+ maxPaths: input?.maxPaths ?? vectorizeDefaultLimits.maxPaths,
29
+ }
30
+
31
+ for (const name of limitNames) {
32
+ const value = resolved[name]
33
+ const hardLimit = vectorizeHardLimits[name]
34
+ if (!Number.isInteger(value) || value < 1 || value > hardLimit) {
35
+ throw new VectorizeError(
36
+ "invalid_input",
37
+ `${name} must be a positive integer no greater than ${hardLimit}.`,
38
+ { hardLimit, name, value },
39
+ )
40
+ }
41
+ }
42
+ return Object.freeze(resolved)
43
+ }
44
+
45
+ export class VectorizeDeadline {
46
+ readonly #deadline: number
47
+
48
+ constructor(durationMs: number) {
49
+ this.#deadline = performance.now() + durationMs
50
+ }
51
+
52
+ assert(stage: string): void {
53
+ if (this.remainingMs() <= 0) {
54
+ throw new VectorizeError("timeout", `Vectorization timed out during ${stage}.`, { stage })
55
+ }
56
+ }
57
+
58
+ remainingMs(): number {
59
+ return Math.max(0, Math.ceil(this.#deadline - performance.now()))
60
+ }
61
+ }
@@ -0,0 +1,351 @@
1
+ import { createHash } from "node:crypto"
2
+ import { VectorizeError } from "./types.js"
3
+
4
+ export function sha256(input: string | Uint8Array): string {
5
+ return createHash("sha256").update(input).digest("hex")
6
+ }
7
+
8
+ export function sanitizedTraceRgba(rgba: Uint8Array, minimumAlpha: number): Uint8Array {
9
+ if (rgba.length === 0 || rgba.length % 4 !== 0) {
10
+ throw new VectorizeError("invalid_input", "Trace sanitation requires nonempty RGBA pixels.")
11
+ }
12
+ if (!Number.isInteger(minimumAlpha) || minimumAlpha < 1 || minimumAlpha > 64) {
13
+ throw new VectorizeError("invalid_input", "alphaCutoff must be an integer from 1 through 64.")
14
+ }
15
+ const sanitized = Uint8Array.from(rgba)
16
+ for (let index = 0; index < sanitized.length; index += 4) {
17
+ if (sanitized[index + 3]! >= minimumAlpha) continue
18
+ sanitized[index] = 0
19
+ sanitized[index + 1] = 0
20
+ sanitized[index + 2] = 0
21
+ sanitized[index + 3] = 0
22
+ }
23
+ return sanitized
24
+ }
25
+
26
+ export function alphaPlaneTraceRgba(rgba: Uint8Array): Uint8Array {
27
+ if (rgba.length === 0 || rgba.length % 4 !== 0) {
28
+ throw new VectorizeError("invalid_input", "An alpha trace requires nonempty RGBA pixels.")
29
+ }
30
+ const grayscale = new Uint8Array(rgba.length)
31
+ for (let index = 0; index < rgba.length; index += 4) {
32
+ const alpha = rgba[index + 3]!
33
+ grayscale[index] = alpha
34
+ grayscale[index + 1] = alpha
35
+ grayscale[index + 2] = alpha
36
+ grayscale[index + 3] = 255
37
+ }
38
+ return grayscale
39
+ }
40
+
41
+ export function lowAlphaMassRatio(rgba: Uint8Array, cutoff: number): number {
42
+ if (rgba.length === 0 || rgba.length % 4 !== 0) {
43
+ throw new VectorizeError("invalid_input", "Low-alpha measurement requires nonempty RGBA pixels.")
44
+ }
45
+ if (!Number.isInteger(cutoff) || cutoff < 1 || cutoff > 255) {
46
+ throw new VectorizeError("invalid_input", "The low-alpha cutoff must be a byte.")
47
+ }
48
+ let lowAlphaMass = 0
49
+ let visibleAlphaMass = 0
50
+ for (let index = 3; index < rgba.length; index += 4) {
51
+ const alpha = rgba[index]!
52
+ visibleAlphaMass += alpha
53
+ if (alpha > 0 && alpha < cutoff) lowAlphaMass += alpha
54
+ }
55
+ return visibleAlphaMass === 0 ? 0 : lowAlphaMass / visibleAlphaMass
56
+ }
57
+
58
+ export function hasFractionalAlpha(rgba: Uint8Array): boolean {
59
+ for (let index = 3; index < rgba.length; index += 4) {
60
+ const alpha = rgba[index]!
61
+ if (alpha > 0 && alpha < 255) return true
62
+ }
63
+ return false
64
+ }
65
+
66
+ export function normalizedPremultipliedRmse(
67
+ source: Uint8Array,
68
+ candidate: Uint8Array,
69
+ ): number {
70
+ assertComparableRgba(source, candidate, "RMSE")
71
+ let squaredError = 0
72
+ for (let index = 0; index < source.length; index += 4) {
73
+ const sourceAlpha = source[index + 3]! / 255
74
+ const candidateAlpha = candidate[index + 3]! / 255
75
+ for (let channel = 0; channel < 3; channel += 1) {
76
+ const difference =
77
+ source[index + channel]! * sourceAlpha - candidate[index + channel]! * candidateAlpha
78
+ squaredError += difference * difference
79
+ }
80
+ const alphaDifference = source[index + 3]! - candidate[index + 3]!
81
+ squaredError += alphaDifference * alphaDifference
82
+ }
83
+ return Math.sqrt(squaredError / source.length) / 255
84
+ }
85
+
86
+ export function normalizedAlphaRmse(source: Uint8Array, candidate: Uint8Array): number {
87
+ assertComparableRgba(source, candidate, "Alpha RMSE")
88
+ let squaredError = 0
89
+ for (let index = 3; index < source.length; index += 4) {
90
+ const difference = source[index]! - candidate[index]!
91
+ squaredError += difference * difference
92
+ }
93
+ return Math.sqrt(squaredError / (source.length / 4)) / 255
94
+ }
95
+
96
+ function assertComparableRgba(source: Uint8Array, candidate: Uint8Array, label: string): void {
97
+ if (source.length === 0 || source.length !== candidate.length || source.length % 4 !== 0) {
98
+ throw new VectorizeError(
99
+ "invalid_input",
100
+ `${label} inputs must be equally sized nonempty RGBA buffers.`,
101
+ )
102
+ }
103
+ }
104
+
105
+ export interface SupportMetrics {
106
+ readonly outsideAlphaRatio: number
107
+ readonly supportRecall: number
108
+ }
109
+
110
+ export function measureSupport(
111
+ source: Uint8Array,
112
+ candidate: Uint8Array,
113
+ width: number,
114
+ height: number,
115
+ dilation = 1,
116
+ ): SupportMetrics {
117
+ if (
118
+ !Number.isInteger(width) ||
119
+ !Number.isInteger(height) ||
120
+ width < 1 ||
121
+ height < 1 ||
122
+ source.length !== width * height * 4 ||
123
+ candidate.length !== source.length
124
+ ) {
125
+ throw new VectorizeError(
126
+ "invalid_input",
127
+ "Support metrics require equally sized rectangular RGBA buffers.",
128
+ )
129
+ }
130
+ if (!Number.isInteger(dilation) || dilation < 0 || dilation > 4) {
131
+ throw new VectorizeError("invalid_input", "Support dilation must be an integer from 0 to 4.")
132
+ }
133
+
134
+ const support = new Uint8Array(width * height)
135
+ for (let y = 0; y < height; y += 1) {
136
+ for (let x = 0; x < width; x += 1) {
137
+ let covered = false
138
+ for (let dy = -dilation; dy <= dilation && !covered; dy += 1) {
139
+ for (let dx = -dilation; dx <= dilation; dx += 1) {
140
+ const neighborX = x + dx
141
+ const neighborY = y + dy
142
+ if (
143
+ neighborX >= 0 &&
144
+ neighborX < width &&
145
+ neighborY >= 0 &&
146
+ neighborY < height &&
147
+ source[(neighborY * width + neighborX) * 4 + 3]! > 0
148
+ ) {
149
+ covered = true
150
+ break
151
+ }
152
+ }
153
+ }
154
+ support[y * width + x] = covered ? 1 : 0
155
+ }
156
+ }
157
+
158
+ let candidateAlphaMass = 0
159
+ let outsideAlphaMass = 0
160
+ let recalledAlphaMass = 0
161
+ let sourceAlphaMass = 0
162
+ for (let pixel = 0; pixel < width * height; pixel += 1) {
163
+ const sourceAlpha = source[pixel * 4 + 3]! / 255
164
+ const candidateAlpha = candidate[pixel * 4 + 3]! / 255
165
+ sourceAlphaMass += sourceAlpha
166
+ candidateAlphaMass += candidateAlpha
167
+ recalledAlphaMass += Math.min(sourceAlpha, candidateAlpha)
168
+ if (support[pixel] === 0) outsideAlphaMass += candidateAlpha
169
+ }
170
+ if (sourceAlphaMass === 0 || candidateAlphaMass === 0) {
171
+ throw new VectorizeError("quality_limit", "Source and candidate must contain visible pixels.")
172
+ }
173
+ return {
174
+ outsideAlphaRatio: outsideAlphaMass / candidateAlphaMass,
175
+ supportRecall: recalledAlphaMass / sourceAlphaMass,
176
+ }
177
+ }
178
+
179
+ type OklabColor = readonly [number, number, number]
180
+
181
+ export interface DominantOklabDuotoneModel {
182
+ readonly cutoff: number
183
+ readonly primary: OklabColor
184
+ readonly primaryShare: number
185
+ }
186
+
187
+ export function parseHexColor(value: string): readonly [number, number, number] {
188
+ const shorthand = value.match(/^#([a-f0-9])([a-f0-9])([a-f0-9])$/iu)
189
+ if (shorthand !== null) {
190
+ return [
191
+ Number.parseInt(`${shorthand[1]}${shorthand[1]}`, 16),
192
+ Number.parseInt(`${shorthand[2]}${shorthand[2]}`, 16),
193
+ Number.parseInt(`${shorthand[3]}${shorthand[3]}`, 16),
194
+ ]
195
+ }
196
+ const full = value.match(/^#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/iu)
197
+ if (full === null) {
198
+ throw new VectorizeError(
199
+ "invalid_input",
200
+ `Expected a #rgb or #rrggbb color, received ${JSON.stringify(value)}.`,
201
+ )
202
+ }
203
+ return [
204
+ Number.parseInt(full[1]!, 16),
205
+ Number.parseInt(full[2]!, 16),
206
+ Number.parseInt(full[3]!, 16),
207
+ ]
208
+ }
209
+
210
+ export function normalizedHexColor(value: string): string {
211
+ return `#${parseHexColor(value)
212
+ .map((channel) => channel.toString(16).padStart(2, "0"))
213
+ .join("")}`
214
+ }
215
+
216
+ function srgbToOklab([red, green, blue]: readonly [number, number, number]): OklabColor {
217
+ const linear = (channel: number): number => {
218
+ const value = channel / 255
219
+ return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4
220
+ }
221
+ const r = linear(red)
222
+ const g = linear(green)
223
+ const b = linear(blue)
224
+ const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b)
225
+ const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b)
226
+ const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b)
227
+ return [
228
+ 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
229
+ 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
230
+ 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,
231
+ ]
232
+ }
233
+
234
+ export function oklabDistance(left: OklabColor, right: OklabColor): number {
235
+ return Math.hypot(left[0] - right[0], left[1] - right[1], left[2] - right[2])
236
+ }
237
+
238
+ interface WeightedColor {
239
+ readonly color: OklabColor
240
+ readonly key: number
241
+ readonly mass: number
242
+ }
243
+
244
+ function otsuDistanceCutoff(colors: readonly WeightedColor[], primary: OklabColor): number {
245
+ const distances = colors.map(({ color, mass }) => ({
246
+ distance: oklabDistance(color, primary),
247
+ mass,
248
+ }))
249
+ const maximum = Math.max(...distances.map(({ distance }) => distance))
250
+ if (maximum === 0) return Number.POSITIVE_INFINITY
251
+
252
+ const histogram = Array.from({ length: 256 }, () => ({ mass: 0, moment: 0 }))
253
+ for (const { distance, mass } of distances) {
254
+ const index = Math.min(255, Math.floor((distance / maximum) * 255))
255
+ histogram[index]!.mass += mass
256
+ histogram[index]!.moment += mass * distance
257
+ }
258
+ const totalMass = histogram.reduce((sum, bin) => sum + bin.mass, 0)
259
+ const totalMoment = histogram.reduce((sum, bin) => sum + bin.moment, 0)
260
+ let nearMass = 0
261
+ let nearMoment = 0
262
+ let bestScore = -1
263
+ let bestIndex = 254
264
+ for (let index = 0; index < 255; index += 1) {
265
+ const bin = histogram[index]!
266
+ nearMass += bin.mass
267
+ nearMoment += bin.moment
268
+ const farMass = totalMass - nearMass
269
+ if (nearMass === 0 || farMass === 0) continue
270
+ const nearMean = nearMoment / nearMass
271
+ const farMean = (totalMoment - nearMoment) / farMass
272
+ const score = nearMass * farMass * (nearMean - farMean) ** 2
273
+ if (score > bestScore) {
274
+ bestScore = score
275
+ bestIndex = index
276
+ }
277
+ }
278
+ let cutoff = (maximum * (bestIndex + 1)) / 255
279
+ const nearShare = (): number =>
280
+ distances.reduce(
281
+ (sum, { distance, mass }) => sum + (distance <= cutoff ? mass : 0),
282
+ 0,
283
+ ) / totalMass
284
+ if (nearShare() < 0.5) {
285
+ let cumulativeMass = 0
286
+ for (const { distance, mass } of [...distances].sort(
287
+ (left, right) => left.distance - right.distance,
288
+ )) {
289
+ cumulativeMass += mass
290
+ cutoff = distance
291
+ if (cumulativeMass >= totalMass / 2) break
292
+ }
293
+ }
294
+ return cutoff
295
+ }
296
+
297
+ export function dominantOklabDuotoneModel(
298
+ rgba: Uint8Array,
299
+ ): DominantOklabDuotoneModel {
300
+ if (rgba.length === 0 || rgba.length % 4 !== 0) {
301
+ throw new VectorizeError("invalid_input", "A duotone model requires nonempty RGBA pixels.")
302
+ }
303
+ const bins = new Map<
304
+ number,
305
+ { blue: number; green: number; mass: number; red: number }
306
+ >()
307
+ for (let index = 0; index < rgba.length; index += 4) {
308
+ const alpha = rgba[index + 3]! / 255
309
+ if (alpha === 0) continue
310
+ const red = rgba[index]!
311
+ const green = rgba[index + 1]!
312
+ const blue = rgba[index + 2]!
313
+ const key = ((red >> 4) << 8) | ((green >> 4) << 4) | (blue >> 4)
314
+ const bin = bins.get(key) ?? { blue: 0, green: 0, mass: 0, red: 0 }
315
+ bin.mass += alpha
316
+ bin.red += red * alpha
317
+ bin.green += green * alpha
318
+ bin.blue += blue * alpha
319
+ bins.set(key, bin)
320
+ }
321
+ if (bins.size === 0) {
322
+ throw new VectorizeError("invalid_input", "A duotone model requires visible pixels.")
323
+ }
324
+ const colors = [...bins.entries()]
325
+ .map(([key, bin]) => ({
326
+ color: srgbToOklab([
327
+ bin.red / bin.mass,
328
+ bin.green / bin.mass,
329
+ bin.blue / bin.mass,
330
+ ]),
331
+ key,
332
+ mass: bin.mass,
333
+ }))
334
+ .sort((left, right) => right.mass - left.mass || left.key - right.key)
335
+ const primary = colors[0]!.color
336
+ const cutoff = otsuDistanceCutoff(colors, primary)
337
+ const totalMass = colors.reduce((sum, { mass }) => sum + mass, 0)
338
+ const primaryMass = colors.reduce(
339
+ (sum, { color, mass }) =>
340
+ sum + (oklabDistance(color, primary) <= cutoff ? mass : 0),
341
+ 0,
342
+ )
343
+ return { cutoff, primary, primaryShare: primaryMass / totalMass }
344
+ }
345
+
346
+ export function colorBelongsToPrimary(
347
+ rgb: readonly [number, number, number],
348
+ model: DominantOklabDuotoneModel,
349
+ ): boolean {
350
+ return oklabDistance(srgbToOklab(rgb), model.primary) <= model.cutoff
351
+ }