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.
- package/AGENTS.md +20 -0
- package/CHANGELOG.md +197 -2
- package/LICENSE +1 -1
- package/README.md +73 -1
- package/bin/davinci-resolve-advanced-mcp.mjs +20 -0
- package/docs/README.md +19 -0
- package/docs/SKILL.md +62 -1
- package/docs/contributing.md +1 -1
- package/docs/guides/control-panel.md +9 -0
- package/docs/kernels/README.md +21 -0
- package/docs/kernels/audio-fairlight-kernel.md +22 -0
- package/docs/kernels/color-grade-kernel.md +51 -0
- package/docs/kernels/fusion-composition-kernel.md +17 -0
- package/docs/kernels/media-pool-ingest-kernel.md +20 -0
- package/docs/kernels/render-deliver-kernel.md +32 -0
- package/docs/kernels/timeline-conform-interchange-kernel.md +39 -0
- package/docs/kernels/timeline-edit-kernel.md +17 -0
- package/docs/process/release-process.md +29 -7
- package/docs/reference/api-coverage.md +1 -1
- package/docs/reference/api-limitations.md +38 -1
- package/install.py +27 -4
- package/package.json +23 -2
- package/resolve-advanced/README.md +194 -0
- package/resolve-advanced/package.json +50 -0
- package/resolve-advanced/server/black-balance.mjs +92 -0
- package/resolve-advanced/server/capabilities.mjs +86 -0
- package/resolve-advanced/server/cdl-io.mjs +76 -0
- package/resolve-advanced/server/contrast-normalize.mjs +180 -0
- package/resolve-advanced/server/db-patch.mjs +91 -0
- package/resolve-advanced/server/deliverable-entities.mjs +86 -0
- package/resolve-advanced/server/deliverable-qc.mjs +270 -0
- package/resolve-advanced/server/editorial.mjs +370 -0
- package/resolve-advanced/server/exposure-level.mjs +129 -0
- package/resolve-advanced/server/extract-frames.mjs +196 -0
- package/resolve-advanced/server/ffprobe-media.mjs +88 -0
- package/resolve-advanced/server/gamut-legal.mjs +112 -0
- package/resolve-advanced/server/grade-body-patch.mjs +246 -0
- package/resolve-advanced/server/grade-transfer.mjs +66 -0
- package/resolve-advanced/server/group-grade-read.mjs +137 -0
- package/resolve-advanced/server/index.mjs +105 -0
- package/resolve-advanced/server/lib.mjs +84 -0
- package/resolve-advanced/server/libs.mjs +65 -0
- package/resolve-advanced/server/lineage-db.mjs +479 -0
- package/resolve-advanced/server/lut-apply.mjs +61 -0
- package/resolve-advanced/server/match-to-reference.mjs +173 -0
- package/resolve-advanced/server/media-inventory.mjs +169 -0
- package/resolve-advanced/server/media-ops.mjs +243 -0
- package/resolve-advanced/server/node-meta-db.mjs +62 -0
- package/resolve-advanced/server/node-provenance.mjs +102 -0
- package/resolve-advanced/server/offline-ref-db.mjs +420 -0
- package/resolve-advanced/server/offline-ref.mjs +179 -0
- package/resolve-advanced/server/project-db.mjs +233 -0
- package/resolve-advanced/server/provenance-audit.mjs +210 -0
- package/resolve-advanced/server/qc-frame.mjs +224 -0
- package/resolve-advanced/server/qc-sampler.mjs +67 -0
- package/resolve-advanced/server/readback.mjs +77 -0
- package/resolve-advanced/server/render-manifest.mjs +93 -0
- package/resolve-advanced/server/reverse-clip-db.mjs +270 -0
- package/resolve-advanced/server/runner-apply-contract.mjs +64 -0
- package/resolve-advanced/server/runner.mjs +244 -0
- package/resolve-advanced/server/saturation-match.mjs +108 -0
- package/resolve-advanced/server/scope-read.mjs +257 -0
- package/resolve-advanced/server/season-look.mjs +96 -0
- package/resolve-advanced/server/shot-intent.mjs +82 -0
- package/resolve-advanced/server/shot-match.mjs +147 -0
- package/resolve-advanced/server/skin-match.mjs +205 -0
- package/resolve-advanced/server/spec-compile.mjs +217 -0
- package/resolve-advanced/server/tone-curve-transfer.mjs +184 -0
- package/resolve-advanced/server/tool-catalog.mjs +265 -0
- package/resolve-advanced/server/tools/audio.mjs +54 -0
- package/resolve-advanced/server/tools/audio_plan.mjs +58 -0
- package/resolve-advanced/server/tools/capabilities.mjs +16 -0
- package/resolve-advanced/server/tools/color_trace.mjs +165 -0
- package/resolve-advanced/server/tools/conform.mjs +291 -0
- package/resolve-advanced/server/tools/deliverable.mjs +105 -0
- package/resolve-advanced/server/tools/drp.mjs +232 -0
- package/resolve-advanced/server/tools/drt.mjs +200 -0
- package/resolve-advanced/server/tools/drx.mjs +943 -0
- package/resolve-advanced/server/tools/editorial.mjs +69 -0
- package/resolve-advanced/server/tools/fairlight.mjs +108 -0
- package/resolve-advanced/server/tools/fusion.mjs +64 -0
- package/resolve-advanced/server/tools/media.mjs +102 -0
- package/resolve-advanced/server/tools/offline_ref.mjs +123 -0
- package/resolve-advanced/server/tools/pipeline.mjs +137 -0
- package/resolve-advanced/server/tools/project_db.mjs +200 -0
- package/resolve-advanced/server/tools/project_read.mjs +212 -0
- package/resolve-advanced/server/tools/provenance.mjs +73 -0
- package/resolve-advanced/server/verify-grade.mjs +113 -0
- package/resolve-advanced/server/white-balance-match.mjs +122 -0
- package/resolve-advanced/vendor/audio/format-converter.js +330 -0
- package/resolve-advanced/vendor/audio/split.js +173 -0
- package/resolve-advanced/vendor/audio/trim.js +188 -0
- package/resolve-advanced/vendor/audio-fairlight/index.js +391 -0
- package/resolve-advanced/vendor/conform-qc/adapters/frame-sampler.js +56 -0
- package/resolve-advanced/vendor/conform-qc/adapters/local-ffmpeg-sampler.js +65 -0
- package/resolve-advanced/vendor/conform-qc/adapters/rendernode-ffmpeg-sampler.js +50 -0
- package/resolve-advanced/vendor/conform-qc/adapters/resolve/driver.py +176 -0
- package/resolve-advanced/vendor/conform-qc/adapters/resolve-driver.js +61 -0
- package/resolve-advanced/vendor/conform-qc/adapters/resolve-headless-driver.js +86 -0
- package/resolve-advanced/vendor/conform-qc/adapters/vision-validator.js +75 -0
- package/resolve-advanced/vendor/conform-qc/cli.js +63 -0
- package/resolve-advanced/vendor/conform-qc/compare/advisory.js +106 -0
- package/resolve-advanced/vendor/conform-qc/compare/decode.js +36 -0
- package/resolve-advanced/vendor/conform-qc/compare/index.js +61 -0
- package/resolve-advanced/vendor/conform-qc/compare/locate.js +70 -0
- package/resolve-advanced/vendor/conform-qc/compare/metrics.js +409 -0
- package/resolve-advanced/vendor/conform-qc/docs/runbook.md +72 -0
- package/resolve-advanced/vendor/conform-qc/index.js +49 -0
- package/resolve-advanced/vendor/conform-qc/knowledge/index.js +110 -0
- package/resolve-advanced/vendor/conform-qc/ops/conform-core.js +137 -0
- package/resolve-advanced/vendor/conform-qc/ops/conform-diff.js +109 -0
- package/resolve-advanced/vendor/conform-qc/ops/insert.js +108 -0
- package/resolve-advanced/vendor/conform-qc/ops/local-conform.js +61 -0
- package/resolve-advanced/vendor/conform-qc/ops/patch.js +80 -0
- package/resolve-advanced/vendor/conform-qc/ops/trigger.js +32 -0
- package/resolve-advanced/vendor/conform-qc/ops/verify.js +105 -0
- package/resolve-advanced/vendor/conform-qc/ops/workflow.js +30 -0
- package/resolve-advanced/vendor/conform-qc/oracle/emulate.js +155 -0
- package/resolve-advanced/vendor/conform-qc/oracle/index.js +38 -0
- package/resolve-advanced/vendor/conform-qc/oracle/resolve.js +196 -0
- package/resolve-advanced/vendor/conform-qc/package.json +16 -0
- package/resolve-advanced/vendor/conform-qc/packaging/emit-aaf.js +30 -0
- package/resolve-advanced/vendor/conform-qc/packaging/emit-fcp7.js +64 -0
- package/resolve-advanced/vendor/conform-qc/packaging/index.js +169 -0
- package/resolve-advanced/vendor/conform-qc/packaging/media-ops.js +56 -0
- package/resolve-advanced/vendor/conform-qc/packaging/otio.js +50 -0
- package/resolve-advanced/vendor/conform-qc/packaging/relink.js +50 -0
- package/resolve-advanced/vendor/conform-qc/packaging/surgical-relink.js +195 -0
- package/resolve-advanced/vendor/conform-qc/parse/index.js +54 -0
- package/resolve-advanced/vendor/conform-qc/parse/merge-dto.js +27 -0
- package/resolve-advanced/vendor/conform-qc/parse/xmeml-geometry.js +256 -0
- package/resolve-advanced/vendor/conform-qc/reference/burnin-ocr.js +100 -0
- package/resolve-advanced/vendor/conform-qc/reference/sidecar.js +37 -0
- package/resolve-advanced/vendor/conform-qc/repair/analysis.js +62 -0
- package/resolve-advanced/vendor/conform-qc/repair/index.js +78 -0
- package/resolve-advanced/vendor/conform-qc/repair/media-analysis.js +139 -0
- package/resolve-advanced/vendor/conform-qc/repair/media-index.js +135 -0
- package/resolve-advanced/vendor/conform-qc/repair/strategies.js +250 -0
- package/resolve-advanced/vendor/conform-qc/report/index.js +133 -0
- package/resolve-advanced/vendor/conform-qc/roles/index.js +96 -0
- package/resolve-advanced/vendor/conform-qc/synthetic/generate.js +233 -0
- package/resolve-advanced/vendor/conform-qc/test/advisory.test.js +85 -0
- package/resolve-advanced/vendor/conform-qc/test/cli.test.js +39 -0
- package/resolve-advanced/vendor/conform-qc/test/compare.test.js +152 -0
- package/resolve-advanced/vendor/conform-qc/test/conform-diff.test.js +70 -0
- package/resolve-advanced/vendor/conform-qc/test/emulate.test.js +51 -0
- package/resolve-advanced/vendor/conform-qc/test/frame-sampler.test.js +30 -0
- package/resolve-advanced/vendor/conform-qc/test/geometry.test.js +112 -0
- package/resolve-advanced/vendor/conform-qc/test/knowledge.test.js +56 -0
- package/resolve-advanced/vendor/conform-qc/test/lifecycle.test.js +110 -0
- package/resolve-advanced/vendor/conform-qc/test/local-ffmpeg-sampler.test.js +66 -0
- package/resolve-advanced/vendor/conform-qc/test/locate.test.js +63 -0
- package/resolve-advanced/vendor/conform-qc/test/media-analysis.test.js +63 -0
- package/resolve-advanced/vendor/conform-qc/test/media-index-normalized.test.js +65 -0
- package/resolve-advanced/vendor/conform-qc/test/media-index-origin.test.js +53 -0
- package/resolve-advanced/vendor/conform-qc/test/metrics-fast.test.js +91 -0
- package/resolve-advanced/vendor/conform-qc/test/oracle-reverse.test.js +86 -0
- package/resolve-advanced/vendor/conform-qc/test/oracle.test.js +136 -0
- package/resolve-advanced/vendor/conform-qc/test/p0-acceptance.test.js +60 -0
- package/resolve-advanced/vendor/conform-qc/test/p1-acceptance.test.js +75 -0
- package/resolve-advanced/vendor/conform-qc/test/p45-patch-acceptance.test.js +102 -0
- package/resolve-advanced/vendor/conform-qc/test/packaging.test.js +133 -0
- package/resolve-advanced/vendor/conform-qc/test/parse-stubs.test.js +28 -0
- package/resolve-advanced/vendor/conform-qc/test/repair-strategies.test.js +128 -0
- package/resolve-advanced/vendor/conform-qc/test/repair.test.js +84 -0
- package/resolve-advanced/vendor/conform-qc/test/report.test.js +52 -0
- package/resolve-advanced/vendor/conform-qc/test/resolve-readback.test.js +71 -0
- package/resolve-advanced/vendor/conform-qc/test/roles.test.js +107 -0
- package/resolve-advanced/vendor/conform-qc/test/runbook.test.js +30 -0
- package/resolve-advanced/vendor/conform-qc/test/scale-residual.test.js +40 -0
- package/resolve-advanced/vendor/conform-qc/test/sidecar.test.js +51 -0
- package/resolve-advanced/vendor/conform-qc/test/smoke.test.js +87 -0
- package/resolve-advanced/vendor/conform-qc/test/surgical-relink.test.js +73 -0
- package/resolve-advanced/vendor/conform-qc/test/synthetic.test.js +98 -0
- package/resolve-advanced/vendor/conform-qc/test/verify.test.js +93 -0
- package/resolve-advanced/vendor/conform-qc/test/vision-validator.test.js +43 -0
- package/resolve-advanced/vendor/conform-qc/test/workflow-driver.test.js +46 -0
- package/resolve-advanced/vendor/conform-qc/toolset/index.js +53 -0
- package/resolve-advanced/vendor/drp-format/README.md +116 -0
- package/resolve-advanced/vendor/drp-format/__tests__/_resolve-verify.js +39 -0
- package/resolve-advanced/vendor/drp-format/__tests__/add-media-clip.test.js +30 -0
- package/resolve-advanced/vendor/drp-format/__tests__/assemble-timeline.test.js +68 -0
- package/resolve-advanced/vendor/drp-format/__tests__/author-project.test.js +57 -0
- package/resolve-advanced/vendor/drp-format/__tests__/composition-text.test.js +98 -0
- package/resolve-advanced/vendor/drp-format/__tests__/diff.test.js +272 -0
- package/resolve-advanced/vendor/drp-format/__tests__/effect-filters-compressed.test.js +143 -0
- package/resolve-advanced/vendor/drp-format/__tests__/fixtures/p0-3-session30-empty-effectfilters.drp +0 -0
- package/resolve-advanced/vendor/drp-format/__tests__/fixtures/retimed-timemap-50pct.hex +1 -0
- package/resolve-advanced/vendor/drp-format/__tests__/fixtures/retimed-timemap-dynamic.hex +1 -0
- package/resolve-advanced/vendor/drp-format/__tests__/inject-grades.test.js +238 -0
- package/resolve-advanced/vendor/drp-format/__tests__/keyed-dict.test.js +85 -0
- package/resolve-advanced/vendor/drp-format/__tests__/lut-refs.test.js +119 -0
- package/resolve-advanced/vendor/drp-format/__tests__/media-blobs.test.js +25 -0
- package/resolve-advanced/vendor/drp-format/__tests__/media-timemap.test.js +119 -0
- package/resolve-advanced/vendor/drp-format/__tests__/place-fusion-title.test.js +52 -0
- package/resolve-advanced/vendor/drp-format/__tests__/place-generator.test.js +40 -0
- package/resolve-advanced/vendor/drp-format/__tests__/place-transition.test.js +48 -0
- package/resolve-advanced/vendor/drp-format/__tests__/protobuf-wire.test.js +64 -0
- package/resolve-advanced/vendor/drp-format/__tests__/relink-media.test.js +106 -0
- package/resolve-advanced/vendor/drp-format/__tests__/splice-clips.test.js +256 -0
- package/resolve-advanced/vendor/drp-format/assemble-timeline.js +65 -0
- package/resolve-advanced/vendor/drp-format/author-project.js +118 -0
- package/resolve-advanced/vendor/drp-format/composition-text.js +213 -0
- package/resolve-advanced/vendor/drp-format/curves-encoder.js +763 -0
- package/resolve-advanced/vendor/drp-format/diff.js +310 -0
- package/resolve-advanced/vendor/drp-format/drp-packager.js +201 -0
- package/resolve-advanced/vendor/drp-format/drp-validator.js +809 -0
- package/resolve-advanced/vendor/drp-format/effect-encoder.js +640 -0
- package/resolve-advanced/vendor/drp-format/extract-lut-refs.js +119 -0
- package/resolve-advanced/vendor/drp-format/grade-encoder.example.js +133 -0
- package/resolve-advanced/vendor/drp-format/grade-encoder.js +401 -0
- package/resolve-advanced/vendor/drp-format/grade-encoder.test.js +171 -0
- package/resolve-advanced/vendor/drp-format/grade-encoder.verify.js +249 -0
- package/resolve-advanced/vendor/drp-format/grade-node-extractor.js +495 -0
- package/resolve-advanced/vendor/drp-format/grade-parameter-decoder.js +774 -0
- package/resolve-advanced/vendor/drp-format/index.js +174 -0
- package/resolve-advanced/vendor/drp-format/inject-grades.js +219 -0
- package/resolve-advanced/vendor/drp-format/keyed-dict.js +227 -0
- package/resolve-advanced/vendor/drp-format/marker-encoder.js +532 -0
- package/resolve-advanced/vendor/drp-format/media-blobs.js +53 -0
- package/resolve-advanced/vendor/drp-format/media-timemap.js +164 -0
- package/resolve-advanced/vendor/drp-format/mp-folder-builder.js +293 -0
- package/resolve-advanced/vendor/drp-format/node-tree-encoder.js +1246 -0
- package/resolve-advanced/vendor/drp-format/package.json +18 -0
- package/resolve-advanced/vendor/drp-format/place-fusion-title.js +131 -0
- package/resolve-advanced/vendor/drp-format/place-generator.js +77 -0
- package/resolve-advanced/vendor/drp-format/place-transition.js +91 -0
- package/resolve-advanced/vendor/drp-format/power-window-encoder.js +776 -0
- package/resolve-advanced/vendor/drp-format/protobuf-wire.js +111 -0
- package/resolve-advanced/vendor/drp-format/qualifier-encoder.js +706 -0
- package/resolve-advanced/vendor/drp-format/relink-media.js +218 -0
- package/resolve-advanced/vendor/drp-format/rich-title-encoder.js +447 -0
- package/resolve-advanced/vendor/drp-format/seq-container-builder.js +572 -0
- package/resolve-advanced/vendor/drp-format/seq-surgery.js +155 -0
- package/resolve-advanced/vendor/drp-format/splice-clips.js +445 -0
- package/resolve-advanced/vendor/drp-format/templates/empty-project.drp +0 -0
- package/resolve-advanced/vendor/drp-format/templates/fusion-title.xml +56 -0
- package/resolve-advanced/vendor/drp-format/templates/generator-solid-color.xml +21 -0
- package/resolve-advanced/vendor/drp-format/templates/media-clip-h264.drp +0 -0
- package/resolve-advanced/vendor/drp-format/templates/transition-cross-dissolve.xml +22 -0
- package/resolve-advanced/vendor/drp-format/tool-registry.descriptor.js +80 -0
- package/resolve-advanced/vendor/drp-format/utils/safe-archive.js +54 -0
- package/resolve-advanced/vendor/drp-format/xml-builder.js +759 -0
- package/resolve-advanced/vendor/drt-format/__tests__/_resolve-verify.js +21 -0
- package/resolve-advanced/vendor/drt-format/__tests__/build.test.js +113 -0
- package/resolve-advanced/vendor/drt-format/__tests__/multi-format.test.js +93 -0
- package/resolve-advanced/vendor/drt-format/__tests__/parse-real-resolve21.test.js +51 -0
- package/resolve-advanced/vendor/drt-format/__tests__/parse.test.js +134 -0
- package/resolve-advanced/vendor/drt-format/__tests__/resolve-versions.test.js +120 -0
- package/resolve-advanced/vendor/drt-format/__tests__/schema-fingerprint.test.js +62 -0
- package/resolve-advanced/vendor/drt-format/__tests__/smoke.test.js +27 -0
- package/resolve-advanced/vendor/drt-format/__tests__/validate.test.js +109 -0
- package/resolve-advanced/vendor/drt-format/capabilities.js +69 -0
- package/resolve-advanced/vendor/drt-format/capability-domains.js +56 -0
- package/resolve-advanced/vendor/drt-format/drt-builder.js +42 -0
- package/resolve-advanced/vendor/drt-format/drt-parser.js +164 -0
- package/resolve-advanced/vendor/drt-format/drt-validator.js +98 -0
- package/resolve-advanced/vendor/drt-format/index.js +45 -0
- package/resolve-advanced/vendor/drt-format/package.json +17 -0
- package/resolve-advanced/vendor/drt-format/resolve-versions.js +153 -0
- package/resolve-advanced/vendor/drt-format/schema-fingerprint.js +74 -0
- package/resolve-advanced/vendor/drt-format/templates/drt-project-shell.xml +124 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/curves-roundtrip.test.js +148 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-face-refinement.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-filmgrain.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-glow.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-1-lens-flare.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-3-beauty-25.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p2-3-beauty-75.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p5-1-node-no-lut.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/fixtures/p5-1-node-with-lut.drx +58 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/hsl-curves-roundtrip.test.js +117 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/lut-refs-roundtrip.test.js +112 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/matte-roundtrip.test.js +167 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/ofx-roundtrip.test.js +215 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/ofx-slugs-discovery.test.js +149 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/power-window-roundtrip.test.js +230 -0
- package/resolve-advanced/vendor/drx-codec/__tests__/qualifier-roundtrip.test.js +226 -0
- package/resolve-advanced/vendor/drx-codec/cdl-exporter.js +470 -0
- package/resolve-advanced/vendor/drx-codec/color-nlp-parser.js +833 -0
- package/resolve-advanced/vendor/drx-codec/data/color-nlp-action-mappings.json +2232 -0
- package/resolve-advanced/vendor/drx-codec/drx-generator.js +4123 -0
- package/resolve-advanced/vendor/drx-codec/drx-merger.js +320 -0
- package/resolve-advanced/vendor/drx-codec/drx-parser.js +1808 -0
- package/resolve-advanced/vendor/drx-codec/extract-custom-curves.js +215 -0
- package/resolve-advanced/vendor/drx-codec/extract-hsl-curves.js +180 -0
- package/resolve-advanced/vendor/drx-codec/extract-lut-refs.js +158 -0
- package/resolve-advanced/vendor/drx-codec/extract-matte-finesse.js +83 -0
- package/resolve-advanced/vendor/drx-codec/extract-ofx-params.js +226 -0
- package/resolve-advanced/vendor/drx-codec/extract-power-window.js +184 -0
- package/resolve-advanced/vendor/drx-codec/extract-qualifier.js +99 -0
- package/resolve-advanced/vendor/drx-codec/index.js +109 -0
- package/resolve-advanced/vendor/drx-codec/node-layout.js +249 -0
- package/resolve-advanced/vendor/drx-codec/resolved-drx-generator.js +393 -0
- package/resolve-advanced/vendor/drx-codec/tool-resolver.js +974 -0
- package/resolve-advanced/vendor/drx-codec/training-examples.js +548 -0
- package/resolve-advanced/vendor/drx-codec/vocabulary-intents.js +539 -0
- package/resolve-advanced/vendor/drx-parameters/CALIBRATION-STATUS.md +319 -0
- package/resolve-advanced/vendor/drx-parameters/DRX-VALUE-SCALING.md +251 -0
- package/resolve-advanced/vendor/drx-parameters/calibrated-ranges.json +17737 -0
- package/resolve-advanced/vendor/drx-parameters/corrector-types.js +120 -0
- package/resolve-advanced/vendor/drx-parameters/index.js +193 -0
- package/resolve-advanced/vendor/drx-parameters/parameter-codec.js +451 -0
- package/resolve-advanced/vendor/drx-parameters/parameter-ids.js +1292 -0
- package/resolve-advanced/vendor/drx-parameters/parameter-ranges.js +570 -0
- package/resolve-advanced/vendor/drx-parameters/parameter-validator.js +392 -0
- package/resolve-advanced/vendor/drx-parameters/resolvefx-registry.json +14973 -0
- package/resolve-advanced/vendor/drx-parameters/tool-registry.descriptor.js +38 -0
- package/resolve-advanced/vendor/drx-parameters/tools/drx-analyzer.js +371 -0
- package/resolve-advanced/vendor/fairlight/index.js +833 -0
- package/resolve-advanced/vendor/fusion-codec/composition-generator.js +401 -0
- package/resolve-advanced/vendor/fusion-codec/templates/blur-region.js +103 -0
- package/resolve-advanced/vendor/fusion-codec/templates/color-correct.js +87 -0
- package/resolve-advanced/vendor/fusion-codec/templates/film-grain.js +106 -0
- package/resolve-advanced/vendor/fusion-codec/templates/lower-third.js +241 -0
- package/resolve-advanced/vendor/fusion-codec/templates/picture-in-picture.js +188 -0
- package/resolve-advanced/vendor/fusion-codec/templates/text-overlay.js +161 -0
- package/resolve-advanced/vendor/fusion-codec/templates/title-card.js +184 -0
- package/resolve-advanced/vendor/fusion-codec/templates/vignette.js +107 -0
- package/resolve-advanced/vendor/fusion-codec/templates/watermark.js +101 -0
- package/scripts/agent-rules/README.md +68 -0
- package/src/analysis_dashboard.py +233 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +479 -29
- package/src/utils/api_truth.py +117 -0
- package/src/utils/cut_ir.py +1 -1
- package/src/utils/failure_tracker.py +0 -2
- 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
|
+
};
|