@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,210 @@
1
+ import { z } from "zod";
2
+
3
+ import {
4
+ EmojiSelectorSchema,
5
+ EmojiOverlaySourceSchema,
6
+ type EmojiOverlaySource,
7
+ type EmojiSelector,
8
+ } from "../contracts/edit";
9
+
10
+ export const EMOJI_ASSET_GENERATION_COMMAND = "bun run emoji-pack:generate";
11
+ export const BRAND_EMOJI_GENERATION_COMMAND = "bun run brand-icons:generate";
12
+ export const LOCAL_EMOJI_PACK_ROOT = "apps/desktop/.generated/emoji-pack";
13
+ export const APPLE_EMOJI_ASSET_ROOT = `${LOCAL_EMOJI_PACK_ROOT}/assets`;
14
+ export const APPLE_EMOJI_CATALOG_PATH =
15
+ `${LOCAL_EMOJI_PACK_ROOT}/apple-emoji-catalog.json`;
16
+ export const APPLE_EMOJI_MANIFEST_PATH =
17
+ `${LOCAL_EMOJI_PACK_ROOT}/emoji-pack-manifest.json`;
18
+
19
+ const Sha256Schema = z.string().regex(/^[a-f0-9]{64}$/u);
20
+ const AppleCatalogItemSchema = z.strictObject({
21
+ emoji: z.string().min(1),
22
+ emojiVersion: z.string().min(1),
23
+ glyphIDs: z.array(z.number().int().positive()).min(1),
24
+ group: z.string().min(1),
25
+ id: z.string().regex(/^[a-f0-9]+(?:-[a-f0-9]+)*$/u),
26
+ name: z.string().min(1),
27
+ qualification: z.string().min(1),
28
+ sourceIndex: z.number().int().nonnegative(),
29
+ sourceKind: z.enum(["coretext-composite", "coretext-render", "sbix"]),
30
+ sourcePngBytes: z.number().int().positive(),
31
+ sourcePngSha256: Sha256Schema,
32
+ subgroup: z.string().min(1),
33
+ });
34
+
35
+ const AppleCatalogSchema = z.strictObject({
36
+ font: z.strictObject({
37
+ copyright: z.string().nullable(),
38
+ postScriptName: z.literal("AppleColorEmoji"),
39
+ sha256: Sha256Schema,
40
+ version: z.string().nullable(),
41
+ }),
42
+ formatVersion: z.literal(1),
43
+ items: z.array(AppleCatalogItemSchema),
44
+ source: z.strictObject({
45
+ file: z.string().min(1),
46
+ rgiCount: z.number().int().positive(),
47
+ sha256: Sha256Schema,
48
+ unicodeVersion: z.string().min(1),
49
+ url: z.string().url(),
50
+ }),
51
+ unsupported: z.array(z.strictObject({
52
+ emoji: z.string().min(1),
53
+ emojiVersion: z.string().min(1),
54
+ group: z.string().min(1),
55
+ id: z.string().min(1),
56
+ name: z.string().min(1),
57
+ qualification: z.string().min(1),
58
+ reason: z.string().min(1),
59
+ sourceIndex: z.number().int().nonnegative(),
60
+ subgroup: z.string().min(1),
61
+ })),
62
+ });
63
+
64
+ const PackManifestSchema = z.strictObject({
65
+ catalogSha256: Sha256Schema,
66
+ formatVersion: z.literal(1),
67
+ items: z.array(z.strictObject({
68
+ atlas: z.number().int().nonnegative(),
69
+ colorSvgSha256: Sha256Schema,
70
+ duotoneSvgSha256: Sha256Schema,
71
+ id: z.string().min(1),
72
+ index: z.number().int().nonnegative(),
73
+ sourcePngSha256: Sha256Schema,
74
+ tile: z.number().int().nonnegative(),
75
+ })),
76
+ outputs: z.array(z.strictObject({
77
+ atlas: z.number().int().nonnegative(),
78
+ bytes: z.number().int().positive(),
79
+ height: z.number().int().positive(),
80
+ path: z.string().min(1),
81
+ sha256: Sha256Schema,
82
+ variant: z.enum(["color", "duotone"]),
83
+ width: z.number().int().positive(),
84
+ })),
85
+ provenance: z.strictObject({
86
+ fontSha256: Sha256Schema,
87
+ sharp: z.string().min(1),
88
+ vips: z.string().min(1),
89
+ vtracerSha256: Sha256Schema,
90
+ vtracerVersion: z.string().min(1),
91
+ }),
92
+ recipe: z.strictObject({
93
+ background: z.string().min(1),
94
+ columns: z.number().int().positive(),
95
+ iconSize: z.number().int().positive(),
96
+ itemsPerAtlas: z.number().int().positive(),
97
+ rows: z.number().int().positive(),
98
+ tileSize: z.number().int().positive(),
99
+ }),
100
+ });
101
+
102
+ export interface EmojiAssetFilePort {
103
+ fileSize(path: string): Promise<number>;
104
+ readText(path: string): Promise<string>;
105
+ }
106
+
107
+ export type EmojiAssetVariant = "native-png" | "color-svg" | "duotone-svg";
108
+
109
+ export interface EmojiAssetProvider {
110
+ resolve(selector: EmojiSelector): Promise<EmojiOverlaySource>;
111
+ }
112
+
113
+ export interface EmojiAssetProviderOptions {
114
+ readonly catalogPath?: string;
115
+ readonly generatedRoot?: string;
116
+ readonly manifestPath?: string;
117
+ readonly variant?: EmojiAssetVariant;
118
+ }
119
+
120
+ export class MissingEmojiAssetsError extends Error {
121
+ readonly command = EMOJI_ASSET_GENERATION_COMMAND;
122
+ readonly path: string;
123
+
124
+ constructor(path: string) {
125
+ super(`Emoji asset is missing at ${path}. Generate the retained local assets with: ${EMOJI_ASSET_GENERATION_COMMAND}`);
126
+ this.name = "MissingEmojiAssetsError";
127
+ this.path = path;
128
+ }
129
+ }
130
+
131
+ function parseJson(text: string, label: string): unknown {
132
+ try {
133
+ return JSON.parse(text);
134
+ } catch (error) {
135
+ throw new SyntaxError(`Invalid ${label} JSON: ${String(error)}`);
136
+ }
137
+ }
138
+
139
+ export function createEmojiAssetProvider(
140
+ files: EmojiAssetFilePort,
141
+ options: EmojiAssetProviderOptions = {},
142
+ ): EmojiAssetProvider {
143
+ const catalogPath = options.catalogPath ?? APPLE_EMOJI_CATALOG_PATH;
144
+ const manifestPath = options.manifestPath ?? APPLE_EMOJI_MANIFEST_PATH;
145
+ const generatedRoot = options.generatedRoot ?? LOCAL_EMOJI_PACK_ROOT;
146
+ const variant = options.variant ?? "color-svg";
147
+
148
+ return {
149
+ async resolve(selectorInput) {
150
+ const selector = EmojiSelectorSchema.parse(selectorInput);
151
+ const [catalogText, manifestText] = await Promise.all([
152
+ files.readText(catalogPath),
153
+ files.readText(manifestPath),
154
+ ]);
155
+ const catalog = AppleCatalogSchema.parse(parseJson(catalogText, "emoji catalog"));
156
+ const manifest = PackManifestSchema.parse(parseJson(manifestText, "emoji manifest"));
157
+ const item = catalog.items.find((candidate) => (
158
+ selector.kind === "unicode"
159
+ ? candidate.emoji === selector.value
160
+ : selector.kind === "name"
161
+ ? candidate.name.toLocaleLowerCase() === selector.value.toLocaleLowerCase()
162
+ : candidate.id === selector.value
163
+ ));
164
+ if (item === undefined) throw new Error(`Emoji selector did not match the local catalog: ${selector.value}`);
165
+ const manifestItem = manifest.items.find(({ id }) => id === item.id);
166
+ if (manifestItem === undefined || manifestItem.sourcePngSha256 !== item.sourcePngSha256) {
167
+ throw new Error(`Emoji manifest does not match catalog item ${item.id}.`);
168
+ }
169
+ const path = variant === "native-png"
170
+ ? `${generatedRoot}/native/${item.id}.png`
171
+ : `${generatedRoot}/assets/${item.id}/${variant === "color-svg" ? "color.svg" : "duotone.svg"}`;
172
+ let bytes: number;
173
+ try {
174
+ bytes = await files.fileSize(path);
175
+ } catch {
176
+ throw new MissingEmojiAssetsError(path);
177
+ }
178
+ const sha256 = variant === "native-png"
179
+ ? item.sourcePngSha256
180
+ : variant === "color-svg"
181
+ ? manifestItem.colorSvgSha256
182
+ : manifestItem.duotoneSvgSha256;
183
+ return EmojiOverlaySourceSchema.parse({
184
+ asset: {
185
+ bytes,
186
+ mediaType: variant === "native-png" ? "image/png" : "image/svg+xml",
187
+ path,
188
+ provenance: {
189
+ command: ["bun", "run", "emoji-pack:generate"],
190
+ generator: "slopcamera-emoji-pack",
191
+ generatorVersion: manifest.provenance.vtracerVersion,
192
+ kind: "generated",
193
+ sourceSha256: item.sourcePngSha256,
194
+ },
195
+ sha256,
196
+ },
197
+ kind: "emoji",
198
+ provider: "apple-emoji-pack",
199
+ selector,
200
+ });
201
+ },
202
+ };
203
+ }
204
+
205
+ export async function resolveEmojiAsset(
206
+ provider: EmojiAssetProvider,
207
+ selector: EmojiSelector,
208
+ ): Promise<EmojiOverlaySource> {
209
+ return await provider.resolve(EmojiSelectorSchema.parse(selector));
210
+ }
@@ -0,0 +1,409 @@
1
+ import {
2
+ CursorSampleEventSchema,
3
+ RecordingEventV1Schema,
4
+ type CursorSampleEvent,
5
+ type Point,
6
+ type RecordingEventV1,
7
+ type Rect,
8
+ type WindowRecord,
9
+ } from "../contracts/recording";
10
+ import type { WindowSelector } from "../contracts/edit";
11
+
12
+ export interface EventQueryOptions {
13
+ readonly endUs?: number;
14
+ readonly limit?: number;
15
+ readonly maxBytes?: number;
16
+ readonly maxLines?: number;
17
+ readonly startUs?: number;
18
+ readonly types?: readonly RecordingEventV1["type"][];
19
+ }
20
+
21
+ const DEFAULT_MAX_BYTES = 16 * 1024 * 1024;
22
+ const DEFAULT_MAX_LINES = 250_000;
23
+ const DEFAULT_LIMIT = 10_000;
24
+ const ABSOLUTE_MAX_BYTES = 256 * 1024 * 1024;
25
+ const ABSOLUTE_MAX_LINES = 2_000_000;
26
+ const ABSOLUTE_MAX_RESULTS = 100_000;
27
+
28
+ function boundedInteger(value: number, maximum: number, label: string): number {
29
+ if (!Number.isSafeInteger(value) || value <= 0 || value > maximum) {
30
+ throw new RangeError(`${label} must be an integer from 1 through ${maximum}.`);
31
+ }
32
+ return value;
33
+ }
34
+
35
+ export function queryEventJsonl(jsonl: string, options: EventQueryOptions = {}): readonly RecordingEventV1[] {
36
+ const maxBytes = boundedInteger(options.maxBytes ?? DEFAULT_MAX_BYTES, ABSOLUTE_MAX_BYTES, "maxBytes");
37
+ const maxLines = boundedInteger(options.maxLines ?? DEFAULT_MAX_LINES, ABSOLUTE_MAX_LINES, "maxLines");
38
+ const limit = boundedInteger(options.limit ?? DEFAULT_LIMIT, ABSOLUTE_MAX_RESULTS, "limit");
39
+ if (new TextEncoder().encode(jsonl).byteLength > maxBytes) {
40
+ throw new RangeError(`Event stream exceeds maxBytes (${maxBytes}).`);
41
+ }
42
+ const lines = jsonl.split(/\r?\n/u);
43
+ if (lines.at(-1) === "") lines.pop();
44
+ if (lines.length > maxLines) throw new RangeError(`Event stream exceeds maxLines (${maxLines}).`);
45
+ const acceptedTypes = options.types === undefined ? null : new Set(options.types);
46
+ const startUs = options.startUs ?? 0;
47
+ const endUs = options.endUs ?? Number.MAX_SAFE_INTEGER;
48
+ if (!Number.isSafeInteger(startUs) || !Number.isSafeInteger(endUs) || startUs < 0 || endUs < startUs) {
49
+ throw new RangeError("Event query source-time bounds are invalid.");
50
+ }
51
+
52
+ const result: RecordingEventV1[] = [];
53
+ let priorSequence = -1;
54
+ let priorSourceTimeUs = -1;
55
+ for (let index = 0; index < lines.length; index += 1) {
56
+ const line = lines[index];
57
+ if (line === undefined || line.trim().length === 0) continue;
58
+ let input: unknown;
59
+ try {
60
+ input = JSON.parse(line);
61
+ } catch (error) {
62
+ throw new SyntaxError(`Invalid event JSON on line ${index + 1}: ${String(error)}`);
63
+ }
64
+ const event = RecordingEventV1Schema.parse(input);
65
+ if (event.sequence <= priorSequence) {
66
+ throw new Error(`Event sequence must increase at line ${index + 1}.`);
67
+ }
68
+ if (event.sourceTimeUs < priorSourceTimeUs) {
69
+ throw new Error(`Event sourceTimeUs must not move backward at line ${index + 1}.`);
70
+ }
71
+ priorSequence = event.sequence;
72
+ priorSourceTimeUs = event.sourceTimeUs;
73
+ if (event.sourceTimeUs < startUs || event.sourceTimeUs >= endUs) continue;
74
+ if (acceptedTypes !== null && !acceptedTypes.has(event.type)) continue;
75
+ result.push(event);
76
+ if (result.length >= limit) break;
77
+ }
78
+ return result;
79
+ }
80
+
81
+ export type TypingSpan = Readonly<{
82
+ bounds: Rect;
83
+ endUs: number;
84
+ fieldId: string;
85
+ secure: boolean;
86
+ startUs: number;
87
+ text: string;
88
+ updates: readonly TypingUpdate[];
89
+ windowId: string;
90
+ }>;
91
+
92
+ export type TypingUpdate = Readonly<{
93
+ bounds: Rect;
94
+ sourceTimeUs: number;
95
+ text: string;
96
+ }>;
97
+
98
+ function deleteLastCodePoint(value: string): string {
99
+ return [...value].slice(0, -1).join("");
100
+ }
101
+
102
+ export function deriveTypingSpans(
103
+ events: readonly RecordingEventV1[],
104
+ idleTimeoutUs: number,
105
+ ): readonly TypingSpan[] {
106
+ if (!Number.isSafeInteger(idleTimeoutUs) || idleTimeoutUs <= 0) {
107
+ throw new RangeError("idleTimeoutUs must be a positive safe integer.");
108
+ }
109
+ const ordered = [...events]
110
+ .sort((left, right) => left.sourceTimeUs - right.sourceTimeUs || left.sequence - right.sequence);
111
+ const spans: TypingSpan[] = [];
112
+ let active: TypingSpan | null = null;
113
+ const closeActive = (boundaryUs: number): void => {
114
+ if (active === null) return;
115
+ const endUs = Math.min(
116
+ active.endUs,
117
+ Math.max(active.startUs, boundaryUs),
118
+ );
119
+ if (endUs > active.startUs && active.updates.length > 0) {
120
+ spans.push({ ...active, endUs });
121
+ }
122
+ active = null;
123
+ };
124
+
125
+ for (const event of ordered) {
126
+ if (active !== null && event.sourceTimeUs > active.endUs) {
127
+ closeActive(active.endUs);
128
+ }
129
+ if (event.type === "focus.changed") {
130
+ const target = event.target;
131
+ if (
132
+ active !== null
133
+ && (
134
+ target.kind !== "public-input"
135
+ || target.windowId !== active.windowId
136
+ || target.fieldId !== active.fieldId
137
+ )
138
+ ) {
139
+ closeActive(event.sourceTimeUs);
140
+ }
141
+ continue;
142
+ }
143
+ if (event.type === "window.snapshot") {
144
+ if (
145
+ active !== null
146
+ && !event.windows.some(window => (
147
+ window.windowId === active?.windowId
148
+ && window.isFocused
149
+ ))
150
+ ) {
151
+ closeActive(event.sourceTimeUs);
152
+ }
153
+ continue;
154
+ }
155
+ if (event.type === "window.changed") {
156
+ if (
157
+ active !== null
158
+ && (
159
+ (event.change.kind === "destroyed"
160
+ && event.change.windowId === active.windowId)
161
+ || (event.change.kind === "focused"
162
+ && event.change.window.windowId !== active.windowId)
163
+ )
164
+ ) {
165
+ closeActive(event.sourceTimeUs);
166
+ }
167
+ continue;
168
+ }
169
+ if (
170
+ event.type === "lifecycle.marker"
171
+ && [
172
+ "failed",
173
+ "pause-requested",
174
+ "paused",
175
+ "segment-closed",
176
+ "stop-requested",
177
+ "stopped",
178
+ ].includes(event.marker)
179
+ ) {
180
+ closeActive(event.sourceTimeUs);
181
+ continue;
182
+ }
183
+ if (
184
+ event.type === "diagnostic.dropped-events"
185
+ && ["lifecycle", "typing", "window"].includes(event.category)
186
+ ) {
187
+ closeActive(event.sourceTimeUs);
188
+ continue;
189
+ }
190
+ if (event.type !== "typing.input") continue;
191
+
192
+ const input = event.input;
193
+ // Forward-delete depends on a cursor selection that capture deliberately
194
+ // does not retain. Hide the callout instead of presenting stale text.
195
+ if (input.action === "delete-forward") {
196
+ closeActive(event.sourceTimeUs);
197
+ continue;
198
+ }
199
+ const sameTarget = active !== null
200
+ && active.windowId === input.windowId
201
+ && active.fieldId === input.fieldId
202
+ && active.secure === input.secure
203
+ && event.sourceTimeUs <= active.endUs;
204
+ if (!sameTarget) {
205
+ closeActive(event.sourceTimeUs);
206
+ active = {
207
+ bounds: input.bounds,
208
+ endUs: event.sourceTimeUs + idleTimeoutUs,
209
+ fieldId: input.fieldId,
210
+ secure: input.secure,
211
+ startUs: event.sourceTimeUs,
212
+ text: input.secure ? "[REDACTED]" : "",
213
+ updates: [],
214
+ windowId: input.windowId,
215
+ };
216
+ }
217
+ if (active === null) continue;
218
+ let text: string = active.text;
219
+ if (!input.secure) {
220
+ if (input.action === "insert") text += input.text;
221
+ else if (input.action === "replace") text = input.text;
222
+ else if (input.action === "delete-backward") text = deleteLastCodePoint(text);
223
+ }
224
+ active = {
225
+ ...active,
226
+ bounds: input.bounds,
227
+ endUs: event.sourceTimeUs + idleTimeoutUs,
228
+ text,
229
+ updates: input.secure && active.updates.length > 0
230
+ ? active.updates
231
+ : [...active.updates, {
232
+ bounds: input.bounds,
233
+ sourceTimeUs: event.sourceTimeUs,
234
+ text: input.secure ? "[REDACTED]" : text,
235
+ }],
236
+ };
237
+ }
238
+ if (active !== null) closeActive(active.endUs);
239
+ return spans;
240
+ }
241
+
242
+ export type SmoothedCursorSample = Readonly<{
243
+ displayId: string;
244
+ nativeTimeUs: number;
245
+ position: Point;
246
+ sourceTimeUs: number;
247
+ visible: boolean;
248
+ }>;
249
+
250
+ export interface CursorSmoothingOptions {
251
+ readonly minDistancePx: number;
252
+ readonly minIntervalUs: number;
253
+ readonly strength: number;
254
+ }
255
+
256
+ export type CursorSampling = "interpolated" | "nearest";
257
+
258
+ export const MAX_CURSOR_INTERPOLATION_GAP_US = 250_000;
259
+
260
+ function pointDistance(left: Point, right: Point): number {
261
+ return Math.hypot(left.x - right.x, left.y - right.y);
262
+ }
263
+
264
+ export function smoothAndDownsampleCursor(
265
+ events: readonly RecordingEventV1[],
266
+ options: CursorSmoothingOptions,
267
+ ): readonly SmoothedCursorSample[] {
268
+ if (
269
+ !Number.isFinite(options.strength)
270
+ || options.strength < 0
271
+ || options.strength > 1
272
+ || !Number.isSafeInteger(options.minIntervalUs)
273
+ || options.minIntervalUs < 0
274
+ || !Number.isFinite(options.minDistancePx)
275
+ || options.minDistancePx < 0
276
+ ) {
277
+ throw new RangeError("Invalid cursor smoothing options.");
278
+ }
279
+ const cursors = events.filter((event): event is CursorSampleEvent => event.type === "cursor.sample")
280
+ .sort((left, right) => left.sourceTimeUs - right.sourceTimeUs || left.sequence - right.sequence);
281
+ const smoothedByDisplay = new Map<string, Point>();
282
+ const lastKeptByDisplay = new Map<string, SmoothedCursorSample>();
283
+ const lastSeenByDisplay = new Map<string, SmoothedCursorSample>();
284
+ const result: SmoothedCursorSample[] = [];
285
+ const smoothingGain = 1 - options.strength * 0.95;
286
+ for (const event of cursors) {
287
+ const prior = smoothedByDisplay.get(event.displayId);
288
+ const position = prior === undefined || options.strength === 0
289
+ ? event.position
290
+ : {
291
+ x: prior.x + (event.position.x - prior.x) * smoothingGain,
292
+ y: prior.y + (event.position.y - prior.y) * smoothingGain,
293
+ };
294
+ smoothedByDisplay.set(event.displayId, position);
295
+ const sample: SmoothedCursorSample = {
296
+ displayId: event.displayId,
297
+ nativeTimeUs: event.nativeTimeUs,
298
+ position,
299
+ sourceTimeUs: event.sourceTimeUs,
300
+ visible: event.visible,
301
+ };
302
+ lastSeenByDisplay.set(event.displayId, sample);
303
+ const lastKept = lastKeptByDisplay.get(event.displayId);
304
+ if (
305
+ lastKept === undefined
306
+ || sample.visible !== lastKept.visible
307
+ || sample.sourceTimeUs - lastKept.sourceTimeUs >= options.minIntervalUs
308
+ || pointDistance(sample.position, lastKept.position) >= options.minDistancePx
309
+ ) {
310
+ result.push(sample);
311
+ lastKeptByDisplay.set(event.displayId, sample);
312
+ }
313
+ }
314
+ for (const [displayId, sample] of lastSeenByDisplay) {
315
+ if (lastKeptByDisplay.get(displayId)?.sourceTimeUs !== sample.sourceTimeUs) result.push(sample);
316
+ }
317
+ return result.sort((left, right) => left.sourceTimeUs - right.sourceTimeUs || left.displayId.localeCompare(right.displayId));
318
+ }
319
+
320
+ function windowStateAt(events: readonly RecordingEventV1[], sourceTimeUs: number): Map<string, WindowRecord> {
321
+ const windows = new Map<string, WindowRecord>();
322
+ const ordered = [...events].sort((left, right) => left.sourceTimeUs - right.sourceTimeUs || left.sequence - right.sequence);
323
+ for (const event of ordered) {
324
+ if (event.sourceTimeUs > sourceTimeUs) break;
325
+ if (event.type === "window.snapshot") {
326
+ windows.clear();
327
+ for (const window of event.windows) windows.set(window.windowId, window);
328
+ } else if (event.type === "window.changed") {
329
+ if (event.change.kind === "destroyed") windows.delete(event.change.windowId);
330
+ else windows.set(event.change.window.windowId, event.change.window);
331
+ }
332
+ }
333
+ return windows;
334
+ }
335
+
336
+ export function findWindowAtTime(
337
+ events: readonly RecordingEventV1[],
338
+ selector: WindowSelector,
339
+ sourceTimeUs: number,
340
+ ): WindowRecord | null {
341
+ const windows = [...windowStateAt(events, sourceTimeUs).values()];
342
+ if (selector.kind === "window-id") return windows.find(({ windowId }) => windowId === selector.windowId) ?? null;
343
+ const candidates = selector.kind === "application"
344
+ ? windows.filter(({ applicationBundleId }) => applicationBundleId === selector.applicationBundleId)
345
+ : windows;
346
+ return candidates.sort((left, right) => Number(right.isFocused) - Number(left.isFocused) || right.layer - left.layer)[0] ?? null;
347
+ }
348
+
349
+ export function findFocusedInputAtTime(
350
+ events: readonly RecordingEventV1[],
351
+ sourceTimeUs: number,
352
+ ): { readonly bounds: Rect; readonly windowId: string } | null {
353
+ const focus = [...events]
354
+ .filter((event) => event.type === "focus.changed" && event.sourceTimeUs <= sourceTimeUs)
355
+ .sort((left, right) => right.sourceTimeUs - left.sourceTimeUs || right.sequence - left.sequence)[0];
356
+ if (focus !== undefined && focus.type === "focus.changed") {
357
+ return focus.target.kind === "none"
358
+ ? null
359
+ : { bounds: focus.target.bounds, windowId: focus.target.windowId };
360
+ }
361
+ const typing = [...events]
362
+ .filter((event) => event.type === "typing.input" && event.sourceTimeUs <= sourceTimeUs)
363
+ .sort((left, right) => right.sourceTimeUs - left.sourceTimeUs || right.sequence - left.sequence)[0];
364
+ if (typing === undefined || typing.type !== "typing.input") return null;
365
+ return { bounds: typing.input.bounds, windowId: typing.input.windowId };
366
+ }
367
+
368
+ export function findCursorAtTime(
369
+ events: readonly RecordingEventV1[],
370
+ sourceTimeUs: number,
371
+ sampling: CursorSampling = "nearest",
372
+ ): CursorSampleEvent | null {
373
+ const cursors = [...events]
374
+ .filter((event): event is CursorSampleEvent => event.type === "cursor.sample")
375
+ .sort((left, right) => left.sourceTimeUs - right.sourceTimeUs || left.sequence - right.sequence);
376
+ const nearest = [...cursors].sort((left, right) => (
377
+ Math.abs(left.sourceTimeUs - sourceTimeUs) - Math.abs(right.sourceTimeUs - sourceTimeUs)
378
+ || Number(left.sourceTimeUs > sourceTimeUs) - Number(right.sourceTimeUs > sourceTimeUs)
379
+ || left.sequence - right.sequence
380
+ ))[0] ?? null;
381
+ if (sampling === "nearest" || nearest === null) return nearest;
382
+
383
+ const before = [...cursors].reverse().find(({ sourceTimeUs: timeUs }) => timeUs <= sourceTimeUs);
384
+ const after = cursors.find(({ sourceTimeUs: timeUs }) => timeUs >= sourceTimeUs);
385
+ if (
386
+ before === undefined
387
+ || after === undefined
388
+ || before.sourceTimeUs === after.sourceTimeUs
389
+ || before.displayId !== after.displayId
390
+ || !before.visible
391
+ || !after.visible
392
+ || after.sourceTimeUs - before.sourceTimeUs > MAX_CURSOR_INTERPOLATION_GAP_US
393
+ ) {
394
+ return nearest;
395
+ }
396
+ const progress = (sourceTimeUs - before.sourceTimeUs) / (after.sourceTimeUs - before.sourceTimeUs);
397
+ return CursorSampleEventSchema.parse({
398
+ displayId: before.displayId,
399
+ nativeTimeUs: Math.round(before.nativeTimeUs + (after.nativeTimeUs - before.nativeTimeUs) * progress),
400
+ position: {
401
+ x: before.position.x + (after.position.x - before.position.x) * progress,
402
+ y: before.position.y + (after.position.y - before.position.y) * progress,
403
+ },
404
+ sequence: nearest.sequence,
405
+ sourceTimeUs,
406
+ type: "cursor.sample",
407
+ visible: true,
408
+ });
409
+ }