davinci-resolve-mcp 2.57.4 → 2.58.0

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 (328) hide show
  1. package/AGENTS.md +20 -0
  2. package/CHANGELOG.md +197 -2
  3. package/LICENSE +1 -1
  4. package/README.md +73 -1
  5. package/bin/davinci-resolve-advanced-mcp.mjs +20 -0
  6. package/docs/README.md +19 -0
  7. package/docs/SKILL.md +62 -1
  8. package/docs/contributing.md +1 -1
  9. package/docs/guides/control-panel.md +9 -0
  10. package/docs/kernels/README.md +21 -0
  11. package/docs/kernels/audio-fairlight-kernel.md +22 -0
  12. package/docs/kernels/color-grade-kernel.md +51 -0
  13. package/docs/kernels/fusion-composition-kernel.md +17 -0
  14. package/docs/kernels/media-pool-ingest-kernel.md +20 -0
  15. package/docs/kernels/render-deliver-kernel.md +32 -0
  16. package/docs/kernels/timeline-conform-interchange-kernel.md +39 -0
  17. package/docs/kernels/timeline-edit-kernel.md +17 -0
  18. package/docs/process/release-process.md +29 -7
  19. package/docs/reference/api-coverage.md +1 -1
  20. package/docs/reference/api-limitations.md +38 -1
  21. package/install.py +27 -4
  22. package/package.json +23 -2
  23. package/resolve-advanced/README.md +194 -0
  24. package/resolve-advanced/package.json +50 -0
  25. package/resolve-advanced/server/black-balance.mjs +92 -0
  26. package/resolve-advanced/server/capabilities.mjs +86 -0
  27. package/resolve-advanced/server/cdl-io.mjs +76 -0
  28. package/resolve-advanced/server/contrast-normalize.mjs +180 -0
  29. package/resolve-advanced/server/db-patch.mjs +91 -0
  30. package/resolve-advanced/server/deliverable-entities.mjs +86 -0
  31. package/resolve-advanced/server/deliverable-qc.mjs +270 -0
  32. package/resolve-advanced/server/editorial.mjs +370 -0
  33. package/resolve-advanced/server/exposure-level.mjs +129 -0
  34. package/resolve-advanced/server/extract-frames.mjs +196 -0
  35. package/resolve-advanced/server/ffprobe-media.mjs +88 -0
  36. package/resolve-advanced/server/gamut-legal.mjs +112 -0
  37. package/resolve-advanced/server/grade-body-patch.mjs +246 -0
  38. package/resolve-advanced/server/grade-transfer.mjs +66 -0
  39. package/resolve-advanced/server/group-grade-read.mjs +137 -0
  40. package/resolve-advanced/server/index.mjs +105 -0
  41. package/resolve-advanced/server/lib.mjs +84 -0
  42. package/resolve-advanced/server/libs.mjs +65 -0
  43. package/resolve-advanced/server/lineage-db.mjs +479 -0
  44. package/resolve-advanced/server/lut-apply.mjs +61 -0
  45. package/resolve-advanced/server/match-to-reference.mjs +173 -0
  46. package/resolve-advanced/server/media-inventory.mjs +169 -0
  47. package/resolve-advanced/server/media-ops.mjs +243 -0
  48. package/resolve-advanced/server/node-meta-db.mjs +62 -0
  49. package/resolve-advanced/server/node-provenance.mjs +102 -0
  50. package/resolve-advanced/server/offline-ref-db.mjs +420 -0
  51. package/resolve-advanced/server/offline-ref.mjs +179 -0
  52. package/resolve-advanced/server/project-db.mjs +233 -0
  53. package/resolve-advanced/server/provenance-audit.mjs +210 -0
  54. package/resolve-advanced/server/qc-frame.mjs +224 -0
  55. package/resolve-advanced/server/qc-sampler.mjs +67 -0
  56. package/resolve-advanced/server/readback.mjs +77 -0
  57. package/resolve-advanced/server/render-manifest.mjs +93 -0
  58. package/resolve-advanced/server/reverse-clip-db.mjs +270 -0
  59. package/resolve-advanced/server/runner-apply-contract.mjs +64 -0
  60. package/resolve-advanced/server/runner.mjs +244 -0
  61. package/resolve-advanced/server/saturation-match.mjs +108 -0
  62. package/resolve-advanced/server/scope-read.mjs +257 -0
  63. package/resolve-advanced/server/season-look.mjs +96 -0
  64. package/resolve-advanced/server/shot-intent.mjs +82 -0
  65. package/resolve-advanced/server/shot-match.mjs +147 -0
  66. package/resolve-advanced/server/skin-match.mjs +205 -0
  67. package/resolve-advanced/server/spec-compile.mjs +217 -0
  68. package/resolve-advanced/server/tone-curve-transfer.mjs +184 -0
  69. package/resolve-advanced/server/tool-catalog.mjs +265 -0
  70. package/resolve-advanced/server/tools/audio.mjs +54 -0
  71. package/resolve-advanced/server/tools/audio_plan.mjs +58 -0
  72. package/resolve-advanced/server/tools/capabilities.mjs +16 -0
  73. package/resolve-advanced/server/tools/color_trace.mjs +165 -0
  74. package/resolve-advanced/server/tools/conform.mjs +291 -0
  75. package/resolve-advanced/server/tools/deliverable.mjs +105 -0
  76. package/resolve-advanced/server/tools/drp.mjs +232 -0
  77. package/resolve-advanced/server/tools/drt.mjs +200 -0
  78. package/resolve-advanced/server/tools/drx.mjs +943 -0
  79. package/resolve-advanced/server/tools/editorial.mjs +69 -0
  80. package/resolve-advanced/server/tools/fairlight.mjs +108 -0
  81. package/resolve-advanced/server/tools/fusion.mjs +64 -0
  82. package/resolve-advanced/server/tools/media.mjs +102 -0
  83. package/resolve-advanced/server/tools/offline_ref.mjs +123 -0
  84. package/resolve-advanced/server/tools/pipeline.mjs +137 -0
  85. package/resolve-advanced/server/tools/project_db.mjs +200 -0
  86. package/resolve-advanced/server/tools/project_read.mjs +212 -0
  87. package/resolve-advanced/server/tools/provenance.mjs +73 -0
  88. package/resolve-advanced/server/verify-grade.mjs +113 -0
  89. package/resolve-advanced/server/white-balance-match.mjs +122 -0
  90. package/resolve-advanced/vendor/audio/format-converter.js +330 -0
  91. package/resolve-advanced/vendor/audio/split.js +173 -0
  92. package/resolve-advanced/vendor/audio/trim.js +188 -0
  93. package/resolve-advanced/vendor/audio-fairlight/index.js +391 -0
  94. package/resolve-advanced/vendor/conform-qc/adapters/frame-sampler.js +56 -0
  95. package/resolve-advanced/vendor/conform-qc/adapters/local-ffmpeg-sampler.js +65 -0
  96. package/resolve-advanced/vendor/conform-qc/adapters/rendernode-ffmpeg-sampler.js +50 -0
  97. package/resolve-advanced/vendor/conform-qc/adapters/resolve/driver.py +176 -0
  98. package/resolve-advanced/vendor/conform-qc/adapters/resolve-driver.js +61 -0
  99. package/resolve-advanced/vendor/conform-qc/adapters/resolve-headless-driver.js +86 -0
  100. package/resolve-advanced/vendor/conform-qc/adapters/vision-validator.js +75 -0
  101. package/resolve-advanced/vendor/conform-qc/cli.js +63 -0
  102. package/resolve-advanced/vendor/conform-qc/compare/advisory.js +106 -0
  103. package/resolve-advanced/vendor/conform-qc/compare/decode.js +36 -0
  104. package/resolve-advanced/vendor/conform-qc/compare/index.js +61 -0
  105. package/resolve-advanced/vendor/conform-qc/compare/locate.js +70 -0
  106. package/resolve-advanced/vendor/conform-qc/compare/metrics.js +409 -0
  107. package/resolve-advanced/vendor/conform-qc/docs/runbook.md +72 -0
  108. package/resolve-advanced/vendor/conform-qc/index.js +49 -0
  109. package/resolve-advanced/vendor/conform-qc/knowledge/index.js +110 -0
  110. package/resolve-advanced/vendor/conform-qc/ops/conform-core.js +137 -0
  111. package/resolve-advanced/vendor/conform-qc/ops/conform-diff.js +109 -0
  112. package/resolve-advanced/vendor/conform-qc/ops/insert.js +108 -0
  113. package/resolve-advanced/vendor/conform-qc/ops/local-conform.js +61 -0
  114. package/resolve-advanced/vendor/conform-qc/ops/patch.js +80 -0
  115. package/resolve-advanced/vendor/conform-qc/ops/trigger.js +32 -0
  116. package/resolve-advanced/vendor/conform-qc/ops/verify.js +105 -0
  117. package/resolve-advanced/vendor/conform-qc/ops/workflow.js +30 -0
  118. package/resolve-advanced/vendor/conform-qc/oracle/emulate.js +155 -0
  119. package/resolve-advanced/vendor/conform-qc/oracle/index.js +38 -0
  120. package/resolve-advanced/vendor/conform-qc/oracle/resolve.js +196 -0
  121. package/resolve-advanced/vendor/conform-qc/package.json +16 -0
  122. package/resolve-advanced/vendor/conform-qc/packaging/emit-aaf.js +30 -0
  123. package/resolve-advanced/vendor/conform-qc/packaging/emit-fcp7.js +64 -0
  124. package/resolve-advanced/vendor/conform-qc/packaging/index.js +169 -0
  125. package/resolve-advanced/vendor/conform-qc/packaging/media-ops.js +56 -0
  126. package/resolve-advanced/vendor/conform-qc/packaging/otio.js +50 -0
  127. package/resolve-advanced/vendor/conform-qc/packaging/relink.js +50 -0
  128. package/resolve-advanced/vendor/conform-qc/packaging/surgical-relink.js +195 -0
  129. package/resolve-advanced/vendor/conform-qc/parse/index.js +54 -0
  130. package/resolve-advanced/vendor/conform-qc/parse/merge-dto.js +27 -0
  131. package/resolve-advanced/vendor/conform-qc/parse/xmeml-geometry.js +256 -0
  132. package/resolve-advanced/vendor/conform-qc/reference/burnin-ocr.js +100 -0
  133. package/resolve-advanced/vendor/conform-qc/reference/sidecar.js +37 -0
  134. package/resolve-advanced/vendor/conform-qc/repair/analysis.js +62 -0
  135. package/resolve-advanced/vendor/conform-qc/repair/index.js +78 -0
  136. package/resolve-advanced/vendor/conform-qc/repair/media-analysis.js +139 -0
  137. package/resolve-advanced/vendor/conform-qc/repair/media-index.js +135 -0
  138. package/resolve-advanced/vendor/conform-qc/repair/strategies.js +250 -0
  139. package/resolve-advanced/vendor/conform-qc/report/index.js +133 -0
  140. package/resolve-advanced/vendor/conform-qc/roles/index.js +96 -0
  141. package/resolve-advanced/vendor/conform-qc/synthetic/generate.js +233 -0
  142. package/resolve-advanced/vendor/conform-qc/test/advisory.test.js +85 -0
  143. package/resolve-advanced/vendor/conform-qc/test/cli.test.js +39 -0
  144. package/resolve-advanced/vendor/conform-qc/test/compare.test.js +152 -0
  145. package/resolve-advanced/vendor/conform-qc/test/conform-diff.test.js +70 -0
  146. package/resolve-advanced/vendor/conform-qc/test/emulate.test.js +51 -0
  147. package/resolve-advanced/vendor/conform-qc/test/frame-sampler.test.js +30 -0
  148. package/resolve-advanced/vendor/conform-qc/test/geometry.test.js +112 -0
  149. package/resolve-advanced/vendor/conform-qc/test/knowledge.test.js +56 -0
  150. package/resolve-advanced/vendor/conform-qc/test/lifecycle.test.js +110 -0
  151. package/resolve-advanced/vendor/conform-qc/test/local-ffmpeg-sampler.test.js +66 -0
  152. package/resolve-advanced/vendor/conform-qc/test/locate.test.js +63 -0
  153. package/resolve-advanced/vendor/conform-qc/test/media-analysis.test.js +63 -0
  154. package/resolve-advanced/vendor/conform-qc/test/media-index-normalized.test.js +65 -0
  155. package/resolve-advanced/vendor/conform-qc/test/media-index-origin.test.js +53 -0
  156. package/resolve-advanced/vendor/conform-qc/test/metrics-fast.test.js +91 -0
  157. package/resolve-advanced/vendor/conform-qc/test/oracle-reverse.test.js +86 -0
  158. package/resolve-advanced/vendor/conform-qc/test/oracle.test.js +136 -0
  159. package/resolve-advanced/vendor/conform-qc/test/p0-acceptance.test.js +60 -0
  160. package/resolve-advanced/vendor/conform-qc/test/p1-acceptance.test.js +75 -0
  161. package/resolve-advanced/vendor/conform-qc/test/p45-patch-acceptance.test.js +102 -0
  162. package/resolve-advanced/vendor/conform-qc/test/packaging.test.js +133 -0
  163. package/resolve-advanced/vendor/conform-qc/test/parse-stubs.test.js +28 -0
  164. package/resolve-advanced/vendor/conform-qc/test/repair-strategies.test.js +128 -0
  165. package/resolve-advanced/vendor/conform-qc/test/repair.test.js +84 -0
  166. package/resolve-advanced/vendor/conform-qc/test/report.test.js +52 -0
  167. package/resolve-advanced/vendor/conform-qc/test/resolve-readback.test.js +71 -0
  168. package/resolve-advanced/vendor/conform-qc/test/roles.test.js +107 -0
  169. package/resolve-advanced/vendor/conform-qc/test/runbook.test.js +30 -0
  170. package/resolve-advanced/vendor/conform-qc/test/scale-residual.test.js +40 -0
  171. package/resolve-advanced/vendor/conform-qc/test/sidecar.test.js +51 -0
  172. package/resolve-advanced/vendor/conform-qc/test/smoke.test.js +87 -0
  173. package/resolve-advanced/vendor/conform-qc/test/surgical-relink.test.js +73 -0
  174. package/resolve-advanced/vendor/conform-qc/test/synthetic.test.js +98 -0
  175. package/resolve-advanced/vendor/conform-qc/test/verify.test.js +93 -0
  176. package/resolve-advanced/vendor/conform-qc/test/vision-validator.test.js +43 -0
  177. package/resolve-advanced/vendor/conform-qc/test/workflow-driver.test.js +46 -0
  178. package/resolve-advanced/vendor/conform-qc/toolset/index.js +53 -0
  179. package/resolve-advanced/vendor/drp-format/README.md +116 -0
  180. package/resolve-advanced/vendor/drp-format/__tests__/_resolve-verify.js +39 -0
  181. package/resolve-advanced/vendor/drp-format/__tests__/add-media-clip.test.js +30 -0
  182. package/resolve-advanced/vendor/drp-format/__tests__/assemble-timeline.test.js +68 -0
  183. package/resolve-advanced/vendor/drp-format/__tests__/author-project.test.js +57 -0
  184. package/resolve-advanced/vendor/drp-format/__tests__/composition-text.test.js +98 -0
  185. package/resolve-advanced/vendor/drp-format/__tests__/diff.test.js +272 -0
  186. package/resolve-advanced/vendor/drp-format/__tests__/effect-filters-compressed.test.js +143 -0
  187. package/resolve-advanced/vendor/drp-format/__tests__/fixtures/p0-3-session30-empty-effectfilters.drp +0 -0
  188. package/resolve-advanced/vendor/drp-format/__tests__/fixtures/retimed-timemap-50pct.hex +1 -0
  189. package/resolve-advanced/vendor/drp-format/__tests__/fixtures/retimed-timemap-dynamic.hex +1 -0
  190. package/resolve-advanced/vendor/drp-format/__tests__/inject-grades.test.js +238 -0
  191. package/resolve-advanced/vendor/drp-format/__tests__/keyed-dict.test.js +85 -0
  192. package/resolve-advanced/vendor/drp-format/__tests__/lut-refs.test.js +119 -0
  193. package/resolve-advanced/vendor/drp-format/__tests__/media-blobs.test.js +25 -0
  194. package/resolve-advanced/vendor/drp-format/__tests__/media-timemap.test.js +119 -0
  195. package/resolve-advanced/vendor/drp-format/__tests__/place-fusion-title.test.js +52 -0
  196. package/resolve-advanced/vendor/drp-format/__tests__/place-generator.test.js +40 -0
  197. package/resolve-advanced/vendor/drp-format/__tests__/place-transition.test.js +48 -0
  198. package/resolve-advanced/vendor/drp-format/__tests__/protobuf-wire.test.js +64 -0
  199. package/resolve-advanced/vendor/drp-format/__tests__/relink-media.test.js +106 -0
  200. package/resolve-advanced/vendor/drp-format/__tests__/splice-clips.test.js +256 -0
  201. package/resolve-advanced/vendor/drp-format/assemble-timeline.js +65 -0
  202. package/resolve-advanced/vendor/drp-format/author-project.js +118 -0
  203. package/resolve-advanced/vendor/drp-format/composition-text.js +213 -0
  204. package/resolve-advanced/vendor/drp-format/curves-encoder.js +763 -0
  205. package/resolve-advanced/vendor/drp-format/diff.js +310 -0
  206. package/resolve-advanced/vendor/drp-format/drp-packager.js +201 -0
  207. package/resolve-advanced/vendor/drp-format/drp-validator.js +809 -0
  208. package/resolve-advanced/vendor/drp-format/effect-encoder.js +640 -0
  209. package/resolve-advanced/vendor/drp-format/extract-lut-refs.js +119 -0
  210. package/resolve-advanced/vendor/drp-format/grade-encoder.example.js +133 -0
  211. package/resolve-advanced/vendor/drp-format/grade-encoder.js +401 -0
  212. package/resolve-advanced/vendor/drp-format/grade-encoder.test.js +171 -0
  213. package/resolve-advanced/vendor/drp-format/grade-encoder.verify.js +249 -0
  214. package/resolve-advanced/vendor/drp-format/grade-node-extractor.js +495 -0
  215. package/resolve-advanced/vendor/drp-format/grade-parameter-decoder.js +774 -0
  216. package/resolve-advanced/vendor/drp-format/index.js +174 -0
  217. package/resolve-advanced/vendor/drp-format/inject-grades.js +219 -0
  218. package/resolve-advanced/vendor/drp-format/keyed-dict.js +227 -0
  219. package/resolve-advanced/vendor/drp-format/marker-encoder.js +532 -0
  220. package/resolve-advanced/vendor/drp-format/media-blobs.js +53 -0
  221. package/resolve-advanced/vendor/drp-format/media-timemap.js +164 -0
  222. package/resolve-advanced/vendor/drp-format/mp-folder-builder.js +293 -0
  223. package/resolve-advanced/vendor/drp-format/node-tree-encoder.js +1246 -0
  224. package/resolve-advanced/vendor/drp-format/package.json +18 -0
  225. package/resolve-advanced/vendor/drp-format/place-fusion-title.js +131 -0
  226. package/resolve-advanced/vendor/drp-format/place-generator.js +77 -0
  227. package/resolve-advanced/vendor/drp-format/place-transition.js +91 -0
  228. package/resolve-advanced/vendor/drp-format/power-window-encoder.js +776 -0
  229. package/resolve-advanced/vendor/drp-format/protobuf-wire.js +111 -0
  230. package/resolve-advanced/vendor/drp-format/qualifier-encoder.js +706 -0
  231. package/resolve-advanced/vendor/drp-format/relink-media.js +218 -0
  232. package/resolve-advanced/vendor/drp-format/rich-title-encoder.js +447 -0
  233. package/resolve-advanced/vendor/drp-format/seq-container-builder.js +572 -0
  234. package/resolve-advanced/vendor/drp-format/seq-surgery.js +155 -0
  235. package/resolve-advanced/vendor/drp-format/splice-clips.js +445 -0
  236. package/resolve-advanced/vendor/drp-format/templates/empty-project.drp +0 -0
  237. package/resolve-advanced/vendor/drp-format/templates/fusion-title.xml +56 -0
  238. package/resolve-advanced/vendor/drp-format/templates/generator-solid-color.xml +21 -0
  239. package/resolve-advanced/vendor/drp-format/templates/media-clip-h264.drp +0 -0
  240. package/resolve-advanced/vendor/drp-format/templates/transition-cross-dissolve.xml +22 -0
  241. package/resolve-advanced/vendor/drp-format/tool-registry.descriptor.js +80 -0
  242. package/resolve-advanced/vendor/drp-format/utils/safe-archive.js +54 -0
  243. package/resolve-advanced/vendor/drp-format/xml-builder.js +759 -0
  244. package/resolve-advanced/vendor/drt-format/__tests__/_resolve-verify.js +21 -0
  245. package/resolve-advanced/vendor/drt-format/__tests__/build.test.js +113 -0
  246. package/resolve-advanced/vendor/drt-format/__tests__/multi-format.test.js +93 -0
  247. package/resolve-advanced/vendor/drt-format/__tests__/parse-real-resolve21.test.js +51 -0
  248. package/resolve-advanced/vendor/drt-format/__tests__/parse.test.js +134 -0
  249. package/resolve-advanced/vendor/drt-format/__tests__/resolve-versions.test.js +120 -0
  250. package/resolve-advanced/vendor/drt-format/__tests__/schema-fingerprint.test.js +62 -0
  251. package/resolve-advanced/vendor/drt-format/__tests__/smoke.test.js +27 -0
  252. package/resolve-advanced/vendor/drt-format/__tests__/validate.test.js +109 -0
  253. package/resolve-advanced/vendor/drt-format/capabilities.js +69 -0
  254. package/resolve-advanced/vendor/drt-format/capability-domains.js +56 -0
  255. package/resolve-advanced/vendor/drt-format/drt-builder.js +42 -0
  256. package/resolve-advanced/vendor/drt-format/drt-parser.js +164 -0
  257. package/resolve-advanced/vendor/drt-format/drt-validator.js +98 -0
  258. package/resolve-advanced/vendor/drt-format/index.js +45 -0
  259. package/resolve-advanced/vendor/drt-format/package.json +17 -0
  260. package/resolve-advanced/vendor/drt-format/resolve-versions.js +153 -0
  261. package/resolve-advanced/vendor/drt-format/schema-fingerprint.js +74 -0
  262. package/resolve-advanced/vendor/drt-format/templates/drt-project-shell.xml +124 -0
  263. package/resolve-advanced/vendor/drx-codec/__tests__/curves-roundtrip.test.js +148 -0
  264. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-face-refinement.drx +58 -0
  265. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-filmgrain.drx +58 -0
  266. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-glow.drx +58 -0
  267. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-lens-flare.drx +58 -0
  268. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-3-beauty-25.drx +58 -0
  269. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-3-beauty-75.drx +58 -0
  270. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p5-1-node-no-lut.drx +58 -0
  271. package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p5-1-node-with-lut.drx +58 -0
  272. package/resolve-advanced/vendor/drx-codec/__tests__/hsl-curves-roundtrip.test.js +117 -0
  273. package/resolve-advanced/vendor/drx-codec/__tests__/lut-refs-roundtrip.test.js +112 -0
  274. package/resolve-advanced/vendor/drx-codec/__tests__/matte-roundtrip.test.js +167 -0
  275. package/resolve-advanced/vendor/drx-codec/__tests__/ofx-roundtrip.test.js +215 -0
  276. package/resolve-advanced/vendor/drx-codec/__tests__/ofx-slugs-discovery.test.js +149 -0
  277. package/resolve-advanced/vendor/drx-codec/__tests__/power-window-roundtrip.test.js +230 -0
  278. package/resolve-advanced/vendor/drx-codec/__tests__/qualifier-roundtrip.test.js +226 -0
  279. package/resolve-advanced/vendor/drx-codec/cdl-exporter.js +470 -0
  280. package/resolve-advanced/vendor/drx-codec/color-nlp-parser.js +833 -0
  281. package/resolve-advanced/vendor/drx-codec/data/color-nlp-action-mappings.json +2232 -0
  282. package/resolve-advanced/vendor/drx-codec/drx-generator.js +4123 -0
  283. package/resolve-advanced/vendor/drx-codec/drx-merger.js +320 -0
  284. package/resolve-advanced/vendor/drx-codec/drx-parser.js +1808 -0
  285. package/resolve-advanced/vendor/drx-codec/extract-custom-curves.js +215 -0
  286. package/resolve-advanced/vendor/drx-codec/extract-hsl-curves.js +180 -0
  287. package/resolve-advanced/vendor/drx-codec/extract-lut-refs.js +158 -0
  288. package/resolve-advanced/vendor/drx-codec/extract-matte-finesse.js +83 -0
  289. package/resolve-advanced/vendor/drx-codec/extract-ofx-params.js +226 -0
  290. package/resolve-advanced/vendor/drx-codec/extract-power-window.js +184 -0
  291. package/resolve-advanced/vendor/drx-codec/extract-qualifier.js +99 -0
  292. package/resolve-advanced/vendor/drx-codec/index.js +109 -0
  293. package/resolve-advanced/vendor/drx-codec/node-layout.js +249 -0
  294. package/resolve-advanced/vendor/drx-codec/resolved-drx-generator.js +393 -0
  295. package/resolve-advanced/vendor/drx-codec/tool-resolver.js +974 -0
  296. package/resolve-advanced/vendor/drx-codec/training-examples.js +548 -0
  297. package/resolve-advanced/vendor/drx-codec/vocabulary-intents.js +539 -0
  298. package/resolve-advanced/vendor/drx-parameters/CALIBRATION-STATUS.md +319 -0
  299. package/resolve-advanced/vendor/drx-parameters/DRX-VALUE-SCALING.md +251 -0
  300. package/resolve-advanced/vendor/drx-parameters/calibrated-ranges.json +17737 -0
  301. package/resolve-advanced/vendor/drx-parameters/corrector-types.js +120 -0
  302. package/resolve-advanced/vendor/drx-parameters/index.js +193 -0
  303. package/resolve-advanced/vendor/drx-parameters/parameter-codec.js +451 -0
  304. package/resolve-advanced/vendor/drx-parameters/parameter-ids.js +1292 -0
  305. package/resolve-advanced/vendor/drx-parameters/parameter-ranges.js +570 -0
  306. package/resolve-advanced/vendor/drx-parameters/parameter-validator.js +392 -0
  307. package/resolve-advanced/vendor/drx-parameters/resolvefx-registry.json +14973 -0
  308. package/resolve-advanced/vendor/drx-parameters/tool-registry.descriptor.js +38 -0
  309. package/resolve-advanced/vendor/drx-parameters/tools/drx-analyzer.js +371 -0
  310. package/resolve-advanced/vendor/fairlight/index.js +833 -0
  311. package/resolve-advanced/vendor/fusion-codec/composition-generator.js +401 -0
  312. package/resolve-advanced/vendor/fusion-codec/templates/blur-region.js +103 -0
  313. package/resolve-advanced/vendor/fusion-codec/templates/color-correct.js +87 -0
  314. package/resolve-advanced/vendor/fusion-codec/templates/film-grain.js +106 -0
  315. package/resolve-advanced/vendor/fusion-codec/templates/lower-third.js +241 -0
  316. package/resolve-advanced/vendor/fusion-codec/templates/picture-in-picture.js +188 -0
  317. package/resolve-advanced/vendor/fusion-codec/templates/text-overlay.js +161 -0
  318. package/resolve-advanced/vendor/fusion-codec/templates/title-card.js +184 -0
  319. package/resolve-advanced/vendor/fusion-codec/templates/vignette.js +107 -0
  320. package/resolve-advanced/vendor/fusion-codec/templates/watermark.js +101 -0
  321. package/scripts/agent-rules/README.md +68 -0
  322. package/src/analysis_dashboard.py +233 -1
  323. package/src/granular/common.py +1 -1
  324. package/src/server.py +479 -29
  325. package/src/utils/api_truth.py +117 -0
  326. package/src/utils/cut_ir.py +1 -1
  327. package/src/utils/failure_tracker.py +0 -2
  328. package/src/utils/timeline_xml.py +405 -0
@@ -0,0 +1,974 @@
1
+ /**
2
+ * Tool Resolver - Unified Tool Selection Layer
3
+ *
4
+ * Central hub that determines which DaVinci Resolve tool to use for any adjustment,
5
+ * whether from AI copilot, manual tweaks, or basic corrections.
6
+ *
7
+ * Design philosophy (from the masters):
8
+ * - Tom Poole: "I work very hard at finding the right primary grade with just offset and lift-gamma-gain"
9
+ * - Sam Daley: "I approach the grade as if I'm in a telecine environment"
10
+ * - Joe Gawler: "Finding the bespoke look for a project drives my process"
11
+ *
12
+ * The tool choice is downstream of intent - this module bridges that gap.
13
+ *
14
+ * @module drx/tool-resolver
15
+ */
16
+
17
+ // ═══════════════════════════════════════════════════════════════════════════════
18
+ // TOOL DEFINITIONS
19
+ // ═══════════════════════════════════════════════════════════════════════════════
20
+
21
+ /**
22
+ * Contrast tool implementations
23
+ * Each tool has a mapping function that converts normalized -1 to +1 values
24
+ * into the actual DRX parameters
25
+ */
26
+ const CONTRAST_TOOLS = {
27
+ /**
28
+ * Lift/Gain Spread - Professional default
29
+ * Creates contrast by lowering shadows (lift) and raising highlights (gain)
30
+ * Preserves midtones, gives clean shadow definition
31
+ */
32
+ liftGainSpread: {
33
+ id: 'liftGainSpread',
34
+ name: 'Lift/Gain Spread',
35
+ description: 'Professional default - clean shadow control, preserves midtones',
36
+ whenToUse: [
37
+ 'Default for most contrast adjustments',
38
+ 'When you want punchy, snappy contrast',
39
+ 'When shadow detail preservation matters',
40
+ ],
41
+ mapping: (value, options = {}) => {
42
+ const intensity = options.intensity || 1.0;
43
+ const scaled = value * intensity;
44
+ return {
45
+ liftMaster: -scaled * 0.25, // Shadows go opposite direction
46
+ gainMaster: scaled * 0.25, // Highlights follow value (added to 1.0 base)
47
+ };
48
+ },
49
+ },
50
+
51
+ /**
52
+ * Contrast Corrector (Type 2)
53
+ * Uses the dedicated contrast slider with pivot control
54
+ * Good for broad, gentle adjustments
55
+ */
56
+ contrastCorrector: {
57
+ id: 'contrastCorrector',
58
+ name: 'Contrast Corrector',
59
+ description: 'Contrast slider + Pivot - broad, gentle adjustments',
60
+ whenToUse: [
61
+ 'Gentle, overall contrast changes',
62
+ 'When pivot point matters',
63
+ 'Quick broadcast-style adjustments',
64
+ ],
65
+ mapping: (value, options = {}) => {
66
+ const intensity = options.intensity || 1.0;
67
+ const pivot = options.pivot || 0.435;
68
+ const scaled = value * intensity;
69
+ return {
70
+ contrast: scaled, // semantic -1 to +1, parseAdjustments converts to Resolve range
71
+ pivot: pivot,
72
+ };
73
+ },
74
+ },
75
+
76
+ /**
77
+ * Lift/Gamma/Gain Full - Maximum tonal control
78
+ * Adjusts all three zones for comprehensive contrast shaping
79
+ */
80
+ liftGammaGainFull: {
81
+ id: 'liftGammaGainFull',
82
+ name: 'Lift/Gamma/Gain Full',
83
+ description: 'All three wheels - maximum tonal control',
84
+ whenToUse: [
85
+ 'Complex contrast shaping',
86
+ 'When midtone adjustment is also needed',
87
+ 'S-curve-like response without actual curves',
88
+ ],
89
+ mapping: (value, options = {}) => {
90
+ const intensity = options.intensity || 1.0;
91
+ const scaled = value * intensity;
92
+ return {
93
+ liftMaster: -scaled * 0.2,
94
+ gammaMaster: scaled * 0.05, // Slight midtone boost for punch
95
+ gainMaster: scaled * 0.2,
96
+ };
97
+ },
98
+ },
99
+
100
+ /**
101
+ * HDR Zones - Zone-based contrast for HDR workflows
102
+ * Adjusts Dark and Highlight zones independently
103
+ */
104
+ hdrZones: {
105
+ id: 'hdrZones',
106
+ name: 'HDR Zones',
107
+ description: 'Zone-based exposure spreads - HDR workflows',
108
+ whenToUse: [
109
+ 'HDR content',
110
+ 'When you need zone-specific control',
111
+ 'Preserving specular highlights while crushing shadows',
112
+ ],
113
+ mapping: (value, options = {}) => {
114
+ const intensity = options.intensity || 1.0;
115
+ const scaled = value * intensity;
116
+ return {
117
+ hdrDark: { exposure: -scaled * 0.8 }, // Stops, -6 to +6
118
+ hdrHighlight: { exposure: scaled * 0.5 },
119
+ };
120
+ },
121
+ },
122
+
123
+ /**
124
+ * Log Wheels Spread - Filmic contrast with smooth transitions
125
+ * Uses log wheels for offset-style contrast
126
+ */
127
+ logWheelsSpread: {
128
+ id: 'logWheelsSpread',
129
+ name: 'Log Wheels Spread',
130
+ description: 'Filmic contrast with smooth tonal transitions',
131
+ whenToUse: [
132
+ 'Log-encoded footage',
133
+ 'Filmic, organic contrast feel',
134
+ 'When you want smoother shadow-to-midtone transitions',
135
+ ],
136
+ mapping: (value, options = {}) => {
137
+ const intensity = options.intensity || 1.0;
138
+ const scaled = value * intensity;
139
+ // Log wheels affect R, G, B equally for luminance-only contrast
140
+ const shadowAmount = -scaled * 0.15;
141
+ const highAmount = scaled * 0.15;
142
+ return {
143
+ logShadowR: shadowAmount,
144
+ logShadowG: shadowAmount,
145
+ logShadowB: shadowAmount,
146
+ logHighR: highAmount,
147
+ logHighG: highAmount,
148
+ logHighB: highAmount,
149
+ };
150
+ },
151
+ },
152
+ };
153
+
154
+ /**
155
+ * Exposure tool implementations
156
+ */
157
+ const EXPOSURE_TOOLS = {
158
+ /**
159
+ * Offset Master - True exposure compensation
160
+ * Affects entire image equally, like changing ISO
161
+ */
162
+ offsetMaster: {
163
+ id: 'offsetMaster',
164
+ name: 'Offset',
165
+ description: 'True exposure compensation - affects entire image equally',
166
+ whenToUse: [
167
+ 'Overall brightness adjustment',
168
+ 'Technical exposure correction',
169
+ 'When you want even adjustment across all tones',
170
+ ],
171
+ mapping: (value, options = {}) => {
172
+ const intensity = options.intensity || 1.0;
173
+ const scaled = value * intensity * 0.2; // Offset is sensitive
174
+ return {
175
+ offsetR: scaled,
176
+ offsetG: scaled,
177
+ offsetB: scaled,
178
+ };
179
+ },
180
+ },
181
+
182
+ /**
183
+ * Gain Master - Highlight-focused brightness
184
+ * Traditional "exposure" control, primarily affects highlights
185
+ */
186
+ gainMaster: {
187
+ id: 'gainMaster',
188
+ name: 'Gain Master',
189
+ description: 'Highlight-focused brightness adjustment',
190
+ whenToUse: [
191
+ 'Brightening highlights specifically',
192
+ 'When shadows should remain stable',
193
+ 'Traditional video-style exposure',
194
+ ],
195
+ mapping: (value, options = {}) => {
196
+ const intensity = options.intensity || 1.0;
197
+ const scaled = value * intensity;
198
+ return {
199
+ gainMaster: scaled * 0.5, // Added to 1.0 base
200
+ };
201
+ },
202
+ },
203
+
204
+ /**
205
+ * Lift/Gamma/Gain Combo - Balanced exposure
206
+ * Distributes exposure change across all tonal ranges
207
+ */
208
+ liftGammaGainCombo: {
209
+ id: 'liftGammaGainCombo',
210
+ name: 'Lift/Gamma/Gain Balanced',
211
+ description: 'Balanced exposure using all three wheels',
212
+ whenToUse: [
213
+ 'Natural-feeling brightness changes',
214
+ 'When you want to maintain tonal relationships',
215
+ 'Filmic exposure adjustments',
216
+ ],
217
+ mapping: (value, options = {}) => {
218
+ const intensity = options.intensity || 1.0;
219
+ const scaled = value * intensity;
220
+ return {
221
+ liftMaster: scaled * 0.12,
222
+ gammaMaster: scaled * 0.25,
223
+ gainMaster: scaled * 0.18,
224
+ };
225
+ },
226
+ },
227
+
228
+ /**
229
+ * HDR Global - HDR global zone exposure
230
+ * Uses HDR wheels global zone for exposure compensation
231
+ */
232
+ hdrGlobal: {
233
+ id: 'hdrGlobal',
234
+ name: 'HDR Global Zone',
235
+ description: 'HDR Global zone exposure - full range control',
236
+ whenToUse: [
237
+ 'HDR workflows',
238
+ 'When working in HDR zone-based grading',
239
+ 'Large exposure adjustments (up to ±6 stops)',
240
+ ],
241
+ mapping: (value, options = {}) => {
242
+ const intensity = options.intensity || 1.0;
243
+ const scaled = value * intensity;
244
+ return {
245
+ hdrGlobal: { exposure: scaled * 3 }, // -6 to +6 stops range
246
+ };
247
+ },
248
+ },
249
+
250
+ /**
251
+ * Gamma Master - Midtone-focused brightness
252
+ * Adjusts perceived brightness without crushing shadows or blowing highlights
253
+ */
254
+ gammaMaster: {
255
+ id: 'gammaMaster',
256
+ name: 'Gamma Master',
257
+ description: 'Midtone-focused brightness - preserves shadow/highlight detail',
258
+ whenToUse: [
259
+ 'Adjusting perceived brightness without clipping',
260
+ 'When shadow and highlight detail must be preserved',
261
+ 'Subtle brightness adjustments',
262
+ ],
263
+ mapping: (value, options = {}) => {
264
+ const intensity = options.intensity || 1.0;
265
+ const scaled = value * intensity;
266
+ return {
267
+ gammaMaster: scaled * 0.35,
268
+ };
269
+ },
270
+ },
271
+ };
272
+
273
+ /**
274
+ * Temperature tool implementations
275
+ */
276
+ const TEMPERATURE_TOOLS = {
277
+ /**
278
+ * Temperature Slider - Global temperature shift
279
+ * Simple, direct Kelvin offset
280
+ */
281
+ temperatureSlider: {
282
+ id: 'temperatureSlider',
283
+ name: 'Temperature Slider',
284
+ description: 'Global temperature shift - direct Kelvin control',
285
+ whenToUse: [
286
+ 'Simple warm/cool adjustments',
287
+ 'Technical white balance correction',
288
+ 'When the whole image needs the same shift',
289
+ ],
290
+ mapping: (value, options = {}) => {
291
+ const intensity = options.intensity || 1.0;
292
+ const scaled = value * intensity;
293
+ return {
294
+ temperature: scaled, // semantic -1 to +1, parseAdjustments converts to wheel values
295
+ };
296
+ },
297
+ },
298
+
299
+ /**
300
+ * Lift/Gain Split - Classic teal/orange
301
+ * Warm highlights, cool shadows (or vice versa)
302
+ */
303
+ liftGainSplit: {
304
+ id: 'liftGainSplit',
305
+ name: 'Lift/Gain Color Split',
306
+ description: 'Classic teal/orange - shadows vs highlights color split',
307
+ whenToUse: [
308
+ 'Cinematic teal and orange look',
309
+ 'When shadows and highlights should have different temperatures',
310
+ 'Creative color grading',
311
+ ],
312
+ mapping: (value, options = {}) => {
313
+ const intensity = options.intensity || 1.0;
314
+ const scaled = value * intensity;
315
+ // Positive = warm highlights, cool shadows
316
+ // Negative = cool highlights, warm shadows
317
+ return {
318
+ liftR: -scaled * 0.08,
319
+ liftB: scaled * 0.08,
320
+ gainR: scaled * 0.08,
321
+ gainB: -scaled * 0.08,
322
+ };
323
+ },
324
+ },
325
+
326
+ /**
327
+ * Log Wheels Temperature - Filmic temperature transitions
328
+ * Uses log wheels for smoother color transitions between tones
329
+ */
330
+ logWheelsTemp: {
331
+ id: 'logWheelsTemp',
332
+ name: 'Log Wheels Temperature',
333
+ description: 'Filmic temperature with smooth transitions',
334
+ whenToUse: [
335
+ 'Log-encoded footage',
336
+ 'When you want subtle, filmic temperature shifts',
337
+ 'Professional film-style color',
338
+ ],
339
+ mapping: (value, options = {}) => {
340
+ const intensity = options.intensity || 1.0;
341
+ const scaled = value * intensity;
342
+ // Warm = add red, remove blue
343
+ return {
344
+ logShadowR: scaled * 0.06,
345
+ logShadowB: -scaled * 0.06,
346
+ logMidR: scaled * 0.04,
347
+ logMidB: -scaled * 0.04,
348
+ logHighR: scaled * 0.06,
349
+ logHighB: -scaled * 0.06,
350
+ };
351
+ },
352
+ },
353
+
354
+ /**
355
+ * Gain RGB Only - Warm/cool highlights only
356
+ * Affects only the highlight range
357
+ */
358
+ gainRGB: {
359
+ id: 'gainRGB',
360
+ name: 'Gain RGB (Highlights)',
361
+ description: 'Temperature shift in highlights only',
362
+ whenToUse: [
363
+ 'Warming or cooling just the highlights',
364
+ 'Sunset/golden hour on bright areas',
365
+ 'When shadows should stay neutral',
366
+ ],
367
+ mapping: (value, options = {}) => {
368
+ const intensity = options.intensity || 1.0;
369
+ const scaled = value * intensity;
370
+ return {
371
+ gainR: scaled * 0.12,
372
+ gainB: -scaled * 0.12,
373
+ };
374
+ },
375
+ },
376
+
377
+ /**
378
+ * Lift RGB Only - Warm/cool shadows only
379
+ * Affects only the shadow range
380
+ */
381
+ liftRGB: {
382
+ id: 'liftRGB',
383
+ name: 'Lift RGB (Shadows)',
384
+ description: 'Temperature shift in shadows only',
385
+ whenToUse: [
386
+ 'Warming or cooling just the shadows',
387
+ 'Adding teal to shadows while keeping highlights neutral',
388
+ 'When highlights should stay neutral',
389
+ ],
390
+ mapping: (value, options = {}) => {
391
+ const intensity = options.intensity || 1.0;
392
+ const scaled = value * intensity;
393
+ return {
394
+ liftR: scaled * 0.1,
395
+ liftB: -scaled * 0.1,
396
+ };
397
+ },
398
+ },
399
+ };
400
+
401
+ /**
402
+ * Saturation tool implementations
403
+ */
404
+ const SATURATION_TOOLS = {
405
+ /**
406
+ * Saturation Slider - Global saturation
407
+ * Simple multiplier on all colors
408
+ */
409
+ saturationSlider: {
410
+ id: 'saturationSlider',
411
+ name: 'Saturation Slider',
412
+ description: 'Global saturation - affects all colors equally',
413
+ whenToUse: [
414
+ 'Simple saturation boost or reduction',
415
+ 'When all colors should be affected equally',
416
+ 'Quick adjustments',
417
+ ],
418
+ mapping: (value, options = {}) => {
419
+ const intensity = options.intensity || 1.0;
420
+ const scaled = value * intensity;
421
+ return {
422
+ saturation: scaled, // semantic -1 to +1, parseAdjustments converts to Resolve range
423
+ };
424
+ },
425
+ },
426
+
427
+ /**
428
+ * Color Boost - Intelligent saturation
429
+ * Protects already-saturated areas from over-saturation
430
+ */
431
+ colorBoost: {
432
+ id: 'colorBoost',
433
+ name: 'Color Boost',
434
+ description: 'Intelligent saturation - protects already-saturated areas',
435
+ whenToUse: [
436
+ 'Boosting saturation without oversaturating',
437
+ 'Skin tone protection',
438
+ 'Natural-looking saturation increases',
439
+ ],
440
+ mapping: (value, options = {}) => {
441
+ const intensity = options.intensity || 1.0;
442
+ const scaled = value * intensity;
443
+ return {
444
+ colorBoost: Math.max(0, scaled * 50), // 0-100 range
445
+ };
446
+ },
447
+ },
448
+
449
+ /**
450
+ * Per-Zone Saturation - HDR zone saturation control
451
+ * Adjusts saturation in specific luminance zones
452
+ */
453
+ hdrZoneSaturation: {
454
+ id: 'hdrZoneSaturation',
455
+ name: 'HDR Zone Saturation',
456
+ description: 'Zone-based saturation - different saturation per luminance zone',
457
+ whenToUse: [
458
+ 'HDR workflows',
459
+ 'When you want less saturation in highlights (natural)',
460
+ 'Zone-specific saturation control',
461
+ ],
462
+ mapping: (value, options = {}) => {
463
+ const intensity = options.intensity || 1.0;
464
+ const scaled = value * intensity;
465
+ // Slightly less saturation boost in highlights (more natural)
466
+ return {
467
+ hdrDark: { saturation: 1.0 + (scaled * 0.4) },
468
+ hdrShadow: { saturation: 1.0 + (scaled * 0.5) },
469
+ hdrLight: { saturation: 1.0 + (scaled * 0.4) },
470
+ hdrHighlight: { saturation: 1.0 + (scaled * 0.3) },
471
+ };
472
+ },
473
+ },
474
+ };
475
+
476
+ // ═══════════════════════════════════════════════════════════════════════════════
477
+ // INTENT MAPPINGS
478
+ // ═══════════════════════════════════════════════════════════════════════════════
479
+
480
+ /**
481
+ * Maps intent keywords to preferred tools
482
+ * These are used when the AI or vocabulary provides intent signals
483
+ */
484
+ const INTENT_TO_TOOL = {
485
+ contrast: {
486
+ // Snappy, punchy, dynamic
487
+ punchy: 'liftGainSpread',
488
+ snappy: 'liftGainSpread',
489
+ dynamic: 'liftGainSpread',
490
+ pop: 'liftGainSpread',
491
+
492
+ // Filmic, organic, smooth
493
+ filmic: 'logWheelsSpread',
494
+ organic: 'logWheelsSpread',
495
+ smooth: 'logWheelsSpread',
496
+ film: 'logWheelsSpread',
497
+
498
+ // Soft, gentle, broadcast
499
+ soft: 'contrastCorrector',
500
+ gentle: 'contrastCorrector',
501
+ broadcast: 'contrastCorrector',
502
+ subtle: 'contrastCorrector',
503
+
504
+ // HDR, zone-based
505
+ hdr: 'hdrZones',
506
+ zone: 'hdrZones',
507
+ zonal: 'hdrZones',
508
+
509
+ // Full control
510
+ full: 'liftGammaGainFull',
511
+ comprehensive: 'liftGammaGainFull',
512
+
513
+ // Default
514
+ default: 'liftGainSpread',
515
+ },
516
+
517
+ exposure: {
518
+ // Overall brightness
519
+ overall: 'offsetMaster',
520
+ global: 'offsetMaster',
521
+ even: 'offsetMaster',
522
+ correction: 'offsetMaster',
523
+
524
+ // Highlight-focused
525
+ highlights: 'gainMaster',
526
+ bright: 'gainMaster',
527
+ hot: 'gainMaster',
528
+
529
+ // Shadow-focused (uses lift)
530
+ shadows: 'liftMaster',
531
+ dark: 'liftMaster',
532
+
533
+ // Midtone-focused
534
+ midtones: 'gammaMaster',
535
+ perceived: 'gammaMaster',
536
+
537
+ // Balanced
538
+ balanced: 'liftGammaGainCombo',
539
+ natural: 'liftGammaGainCombo',
540
+ filmic: 'liftGammaGainCombo',
541
+
542
+ // HDR
543
+ hdr: 'hdrGlobal',
544
+ zone: 'hdrGlobal',
545
+
546
+ // Default
547
+ default: 'offsetMaster',
548
+ },
549
+
550
+ temperature: {
551
+ // Global
552
+ global: 'temperatureSlider',
553
+ overall: 'temperatureSlider',
554
+ correction: 'temperatureSlider',
555
+ whiteBalance: 'temperatureSlider',
556
+
557
+ // Split (teal/orange)
558
+ split: 'liftGainSplit',
559
+ tealOrange: 'liftGainSplit',
560
+ cinematic: 'liftGainSplit',
561
+ blockbuster: 'liftGainSplit',
562
+
563
+ // Filmic
564
+ filmic: 'logWheelsTemp',
565
+ organic: 'logWheelsTemp',
566
+ smooth: 'logWheelsTemp',
567
+
568
+ // Highlight only
569
+ highlightOnly: 'gainRGB',
570
+ highlights: 'gainRGB',
571
+ warmHighlights: 'gainRGB',
572
+ coolHighlights: 'gainRGB',
573
+
574
+ // Shadow only
575
+ shadowOnly: 'liftRGB',
576
+ shadows: 'liftRGB',
577
+ warmShadows: 'liftRGB',
578
+ coolShadows: 'liftRGB',
579
+
580
+ // Default
581
+ default: 'temperatureSlider',
582
+ },
583
+
584
+ saturation: {
585
+ // Global
586
+ global: 'saturationSlider',
587
+ overall: 'saturationSlider',
588
+ simple: 'saturationSlider',
589
+
590
+ // Smart/protected
591
+ smart: 'colorBoost',
592
+ protected: 'colorBoost',
593
+ skinSafe: 'colorBoost',
594
+ natural: 'colorBoost',
595
+
596
+ // Zone-based
597
+ hdr: 'hdrZoneSaturation',
598
+ zone: 'hdrZoneSaturation',
599
+ zonal: 'hdrZoneSaturation',
600
+
601
+ // Default
602
+ default: 'saturationSlider',
603
+ },
604
+ };
605
+
606
+ // ═══════════════════════════════════════════════════════════════════════════════
607
+ // DEFAULT USER PREFERENCES
608
+ // ═══════════════════════════════════════════════════════════════════════════════
609
+
610
+ const DEFAULT_USER_PREFERENCES = {
611
+ // Default tool choices (when no intent specified)
612
+ defaultTools: {
613
+ contrast: 'liftGainSpread',
614
+ exposure: 'offsetMaster',
615
+ temperature: 'temperatureSlider',
616
+ saturation: 'saturationSlider',
617
+ },
618
+
619
+ // Intensity multipliers (1.0 = as-is, >1 = stronger, <1 = subtler)
620
+ intensityMultipliers: {
621
+ contrast: 1.0,
622
+ exposure: 1.0,
623
+ temperature: 1.0,
624
+ saturation: 1.0,
625
+ tint: 1.0,
626
+ shadowLift: 1.0,
627
+ highlightCompression: 1.0,
628
+ },
629
+
630
+ // Wheels mode preference ('primary', 'log', 'hdr')
631
+ wheelsMode: 'primary',
632
+
633
+ // When to use per-channel vs master
634
+ perChannelThreshold: 0.15, // Use per-channel when color splits > this
635
+
636
+ // Learned patterns (populated from training data)
637
+ learnedPatterns: {
638
+ preferredContrastRange: [0.15, 0.4],
639
+ preferredSaturationRange: [0.85, 1.2],
640
+ commonLooks: [],
641
+ },
642
+ };
643
+
644
+ // ═══════════════════════════════════════════════════════════════════════════════
645
+ // TOOL RESOLVER CLASS
646
+ // ═══════════════════════════════════════════════════════════════════════════════
647
+
648
+ class ToolResolver {
649
+ constructor(userPreferences = {}) {
650
+ this.preferences = { ...DEFAULT_USER_PREFERENCES, ...userPreferences };
651
+ this.tools = {
652
+ contrast: CONTRAST_TOOLS,
653
+ exposure: EXPOSURE_TOOLS,
654
+ temperature: TEMPERATURE_TOOLS,
655
+ saturation: SATURATION_TOOLS,
656
+ };
657
+ }
658
+
659
+ /**
660
+ * Update user preferences
661
+ * @param {Object} prefs - New preferences to merge
662
+ */
663
+ setUserPreferences(prefs) {
664
+ this.preferences = {
665
+ ...this.preferences,
666
+ ...prefs,
667
+ defaultTools: { ...this.preferences.defaultTools, ...prefs.defaultTools },
668
+ intensityMultipliers: { ...this.preferences.intensityMultipliers, ...prefs.intensityMultipliers },
669
+ };
670
+ }
671
+
672
+ /**
673
+ * Get the tool to use for a given adjustment type and intent
674
+ * @param {string} adjustmentType - 'contrast', 'exposure', 'temperature', 'saturation'
675
+ * @param {string} intent - Optional intent keyword
676
+ * @returns {string} Tool ID
677
+ */
678
+ selectTool(adjustmentType, intent = null) {
679
+ const intentMap = INTENT_TO_TOOL[adjustmentType];
680
+ if (!intentMap) {
681
+ console.warn(`[ToolResolver] Unknown adjustment type: ${adjustmentType}`);
682
+ return null;
683
+ }
684
+
685
+ // If intent provided, look it up
686
+ if (intent && intentMap[intent]) {
687
+ return intentMap[intent];
688
+ }
689
+
690
+ // Fall back to user's default preference
691
+ const userDefault = this.preferences.defaultTools[adjustmentType];
692
+ if (userDefault) {
693
+ return userDefault;
694
+ }
695
+
696
+ // Ultimate fallback
697
+ return intentMap.default;
698
+ }
699
+
700
+ /**
701
+ * Get the intensity multiplier for an adjustment type
702
+ * @param {string} adjustmentType
703
+ * @returns {number}
704
+ */
705
+ getIntensityMultiplier(adjustmentType) {
706
+ return this.preferences.intensityMultipliers[adjustmentType] || 1.0;
707
+ }
708
+
709
+ /**
710
+ * Resolve a single adjustment to DRX parameters
711
+ * @param {string} adjustmentType - 'contrast', 'exposure', etc.
712
+ * @param {number} value - Normalized value (-1 to +1)
713
+ * @param {Object} options - { intent, pivot, etc. }
714
+ * @returns {Object} DRX parameters
715
+ */
716
+ resolveAdjustment(adjustmentType, value, options = {}) {
717
+ const toolId = this.selectTool(adjustmentType, options.intent);
718
+ const tool = this.tools[adjustmentType]?.[toolId];
719
+
720
+ if (!tool) {
721
+ console.warn(`[ToolResolver] Tool not found: ${adjustmentType}/${toolId}`);
722
+ return {};
723
+ }
724
+
725
+ // Apply intensity multiplier
726
+ const intensity = this.getIntensityMultiplier(adjustmentType) * (options.intensity || 1.0);
727
+
728
+ // Get mapping with options
729
+ const params = tool.mapping(value, { ...options, intensity });
730
+
731
+ // Log for debugging
732
+ console.log(`[ToolResolver] ${adjustmentType}: value=${value}, tool=${toolId}, intent=${options.intent || 'default'}`);
733
+
734
+ return {
735
+ tool: toolId,
736
+ toolName: tool.name,
737
+ params,
738
+ };
739
+ }
740
+
741
+ /**
742
+ * Resolve a full adjustment set (from AI or vocabulary)
743
+ * @param {Object} input - { adjustments: {...}, intent: {...} }
744
+ * @returns {Object} Resolved DRX parameters
745
+ */
746
+ resolve(input) {
747
+ const { adjustments = {}, intent = {} } = input;
748
+ const resolved = {
749
+ params: {},
750
+ tools: {},
751
+ metadata: {
752
+ timestamp: Date.now(),
753
+ preferences: this.preferences.defaultTools,
754
+ },
755
+ };
756
+
757
+ // Process contrast
758
+ if (adjustments.contrast !== undefined && Math.abs(adjustments.contrast) > 0.001) {
759
+ const result = this.resolveAdjustment('contrast', adjustments.contrast, {
760
+ intent: intent.contrast,
761
+ pivot: adjustments.pivot,
762
+ });
763
+ Object.assign(resolved.params, result.params);
764
+ resolved.tools.contrast = result.tool;
765
+ }
766
+
767
+ // Process exposure
768
+ if (adjustments.exposure !== undefined && Math.abs(adjustments.exposure) > 0.001) {
769
+ const result = this.resolveAdjustment('exposure', adjustments.exposure, {
770
+ intent: intent.exposure,
771
+ });
772
+ Object.assign(resolved.params, result.params);
773
+ resolved.tools.exposure = result.tool;
774
+ }
775
+
776
+ // Process temperature
777
+ if (adjustments.temperature !== undefined && Math.abs(adjustments.temperature) > 0.001) {
778
+ const result = this.resolveAdjustment('temperature', adjustments.temperature, {
779
+ intent: intent.temperature,
780
+ });
781
+ Object.assign(resolved.params, result.params);
782
+ resolved.tools.temperature = result.tool;
783
+ }
784
+
785
+ // Process saturation
786
+ if (adjustments.saturation !== undefined && Math.abs(adjustments.saturation) > 0.001) {
787
+ const result = this.resolveAdjustment('saturation', adjustments.saturation, {
788
+ intent: intent.saturation,
789
+ });
790
+ Object.assign(resolved.params, result.params);
791
+ resolved.tools.saturation = result.tool;
792
+ }
793
+
794
+ // Process tint (passthrough — parseAdjustments handles wheel conversion)
795
+ if (adjustments.tint !== undefined && Math.abs(adjustments.tint) > 0.001) {
796
+ const intensity = this.getIntensityMultiplier('tint');
797
+ resolved.params.tint = adjustments.tint * intensity; // semantic -1 to +1, parseAdjustments converts
798
+ }
799
+
800
+ // Process shadow lift (direct to liftMaster unless already set)
801
+ if (adjustments.shadowLift !== undefined && Math.abs(adjustments.shadowLift) > 0.001) {
802
+ const intensity = this.getIntensityMultiplier('shadowLift');
803
+ // Only set if not already set by another tool
804
+ if (resolved.params.liftMaster === undefined) {
805
+ resolved.params.liftMaster = adjustments.shadowLift * intensity * 0.3;
806
+ } else {
807
+ // Add to existing liftMaster
808
+ resolved.params.liftMaster += adjustments.shadowLift * intensity * 0.3;
809
+ }
810
+ }
811
+
812
+ // Process highlight compression
813
+ if (adjustments.highlightCompression !== undefined && adjustments.highlightCompression > 0.001) {
814
+ const intensity = this.getIntensityMultiplier('highlightCompression');
815
+ resolved.params.softClipHigh = 1.0 - (adjustments.highlightCompression * intensity * 0.3);
816
+ }
817
+
818
+ // Process midtone detail (passthrough — parseAdjustments handles scaling)
819
+ if (adjustments.midtoneDetail !== undefined && Math.abs(adjustments.midtoneDetail) > 0.001) {
820
+ resolved.params.midtoneDetail = adjustments.midtoneDetail; // semantic -1 to +1, parseAdjustments scales
821
+ }
822
+
823
+ // Process pivot (passthrough)
824
+ if (adjustments.pivot !== undefined) {
825
+ resolved.params.pivot = adjustments.pivot;
826
+ }
827
+
828
+ // Pass through any per-channel adjustments directly (user explicitly requested)
829
+ this._passPerChannelAdjustments(adjustments, resolved);
830
+
831
+ // Pass through log wheels adjustments directly
832
+ this._passLogWheelsAdjustments(adjustments, resolved);
833
+
834
+ // Pass through HDR zone adjustments
835
+ this._passHdrZoneAdjustments(adjustments, resolved);
836
+
837
+ return resolved;
838
+ }
839
+
840
+ /**
841
+ * Pass through per-channel Lift/Gamma/Gain adjustments
842
+ * These are explicit and bypass tool selection
843
+ */
844
+ _passPerChannelAdjustments(adjustments, resolved) {
845
+ const perChannelKeys = [
846
+ 'liftR', 'liftG', 'liftB',
847
+ 'gammaR', 'gammaG', 'gammaB',
848
+ 'gainR', 'gainG', 'gainB',
849
+ ];
850
+
851
+ for (const key of perChannelKeys) {
852
+ if (adjustments[key] !== undefined && Math.abs(adjustments[key]) > 0.001) {
853
+ // Don't overwrite if already set by tool
854
+ if (resolved.params[key] === undefined) {
855
+ resolved.params[key] = adjustments[key];
856
+ }
857
+ }
858
+ }
859
+
860
+ // Master channels (be careful not to overwrite tool-generated values)
861
+ const masterKeys = ['liftMaster', 'gammaMaster', 'gainMaster'];
862
+ for (const key of masterKeys) {
863
+ if (adjustments[key] !== undefined && Math.abs(adjustments[key]) > 0.001) {
864
+ if (resolved.params[key] === undefined) {
865
+ resolved.params[key] = adjustments[key];
866
+ }
867
+ }
868
+ }
869
+ }
870
+
871
+ /**
872
+ * Pass through log wheels adjustments
873
+ */
874
+ _passLogWheelsAdjustments(adjustments, resolved) {
875
+ const logKeys = [
876
+ 'logShadowR', 'logShadowG', 'logShadowB',
877
+ 'logMidR', 'logMidG', 'logMidB',
878
+ 'logHighR', 'logHighG', 'logHighB',
879
+ ];
880
+
881
+ for (const key of logKeys) {
882
+ if (adjustments[key] !== undefined && Math.abs(adjustments[key]) > 0.001) {
883
+ if (resolved.params[key] === undefined) {
884
+ resolved.params[key] = adjustments[key];
885
+ }
886
+ }
887
+ }
888
+ }
889
+
890
+ /**
891
+ * Pass through HDR zone adjustments
892
+ */
893
+ _passHdrZoneAdjustments(adjustments, resolved) {
894
+ const hdrZones = ['hdrDark', 'hdrShadow', 'hdrLight', 'hdrHighlight', 'hdrGlobal'];
895
+
896
+ for (const zone of hdrZones) {
897
+ if (adjustments[zone] !== undefined) {
898
+ if (resolved.params[zone] === undefined) {
899
+ resolved.params[zone] = adjustments[zone];
900
+ } else {
901
+ // Merge zone properties
902
+ resolved.params[zone] = { ...resolved.params[zone], ...adjustments[zone] };
903
+ }
904
+ }
905
+ }
906
+ }
907
+
908
+ /**
909
+ * Get information about available tools for a given adjustment type
910
+ * @param {string} adjustmentType
911
+ * @returns {Object[]} Array of tool info objects
912
+ */
913
+ getAvailableTools(adjustmentType) {
914
+ const tools = this.tools[adjustmentType];
915
+ if (!tools) return [];
916
+
917
+ return Object.values(tools).map(tool => ({
918
+ id: tool.id,
919
+ name: tool.name,
920
+ description: tool.description,
921
+ whenToUse: tool.whenToUse,
922
+ }));
923
+ }
924
+
925
+ /**
926
+ * Get the current tool selection for an adjustment type
927
+ * @param {string} adjustmentType
928
+ * @returns {Object} Tool info
929
+ */
930
+ getCurrentTool(adjustmentType) {
931
+ const toolId = this.preferences.defaultTools[adjustmentType];
932
+ const tool = this.tools[adjustmentType]?.[toolId];
933
+ return tool ? {
934
+ id: tool.id,
935
+ name: tool.name,
936
+ description: tool.description,
937
+ } : null;
938
+ }
939
+ }
940
+
941
+ // ═══════════════════════════════════════════════════════════════════════════════
942
+ // SINGLETON INSTANCE & EXPORTS
943
+ // ═══════════════════════════════════════════════════════════════════════════════
944
+
945
+ // Create default instance
946
+ const defaultResolver = new ToolResolver();
947
+
948
+ module.exports = {
949
+ // Classes
950
+ ToolResolver,
951
+
952
+ // Default instance
953
+ defaultResolver,
954
+
955
+ // Tool definitions (for inspection/UI)
956
+ CONTRAST_TOOLS,
957
+ EXPOSURE_TOOLS,
958
+ TEMPERATURE_TOOLS,
959
+ SATURATION_TOOLS,
960
+
961
+ // Intent mappings
962
+ INTENT_TO_TOOL,
963
+
964
+ // Default preferences
965
+ DEFAULT_USER_PREFERENCES,
966
+
967
+ // Convenience functions using default resolver
968
+ resolve: (input) => defaultResolver.resolve(input),
969
+ resolveAdjustment: (type, value, options) => defaultResolver.resolveAdjustment(type, value, options),
970
+ selectTool: (type, intent) => defaultResolver.selectTool(type, intent),
971
+ setUserPreferences: (prefs) => defaultResolver.setUserPreferences(prefs),
972
+ getAvailableTools: (type) => defaultResolver.getAvailableTools(type),
973
+ getCurrentTool: (type) => defaultResolver.getCurrentTool(type),
974
+ };