Adjust grouped adjustment layers

What is the possibility of changing the values of a grouped adjustment layer without having to select it.
Here I have a script that goes through all the layers in the group, finds the layer name but I can’t add the values with batchPlay. Any chance? Please share.

const doc = app.activeDocument;
doc.layers.forEach(layer => {
    if (layer.kind == "group") {
        layer.layers.forEach(camada => {
            if (camada.name == "CyanColor") {
                app.batchPlay([{
                    "_obj": "set",
                    "_target": [{
                        "_enum": "ordinal",
                        "_ref": "adjustmentLayer",
                        "_value": "targetEnum"
                    }],
                    "to": {
                        "_obj": "colorBalance",
                        "midtoneLevels": [func.target.value, 0, 0]
                    }
                }], {
                    synchronousExecution: false,
                });
            }
        })
    }
})

Have you gotten any errors outputs in the console or any other debug information? Also, batchPlay is an asynchronous function and you’re calling it without an await, is that deliberate?

Hi @dotproduct I don’t receive any error message in the console, I also followed your tips about “batchPlay being an asynchronous function”, but I was not successful.
My goal is to add some grouped adjustment layers and be able to adjust them with sliders without having to select and avoid opening the group, avoiding clutter in the layers palette.
Here is the plugin and psd template.
AjustColor.zip - Google Drive

Hi @dotproduct I don’t receive any error message in the console and I also made some changes, but it didn’t have any effect.
My goal is to add some adjustment layers to a group and be able to access their values without selecting them, having the group layer as the active layer, accessing its items without having to open the layer tree. Any suggestion?
Here is the UXP plugin and a template file:

I assume you got the original batchPlay descriptor from using Alchemist and/or recording actions? You should probably take a closer look at the UXP docs about batchPlay and about the meaning of different arguments in the descriptor. Especially it talks about the ‘target’ ("_target") of the operation. Right now it looks like you’re operating on the current/default selection ("targetEnum"). You could try operating on a particular layer by passing the layer’s ID. Like so, assuming you’re iterating over adjustment layers and you’re current layer is cur_layer (in your case ‘camada’ :slight_smile: ):

_target: [{ _ref: 'adjustmentLayer', _id: cur_layer.id }],

you need to rewrite target value but thing is bit complicated than I thought.
I tried several times like

_target: [{ _ref: layer', _id: layer.id }],

or

_target: [{ _property: 'adjustmentLayer'}, { _id: layer.id, _ref: "layer" }],

…etc

and finally the code below successfully worked.

const { app } = require("photoshop");
const {executeAsModal} = require("photoshop").core;
const {batchPlay} = require("photoshop").action;

async function actionCommands(id) {
  console.log(id);
   const result = await batchPlay(
      [
         {
            _obj: "set",
             _target: [// focus the layer following the id
               {
                  _ref: "adjustmentLayer",
                  _id: id,
                  _value: "targetEnum"
               }
            ],
            to: {
               _obj: "colorBalance",
               midtoneLevels: [// color balance value
                  0,
                  35,
                  80
               ]
            },
            _options: {
               dialogOptions: "dontDisplay"
            }
         }
      ],
      {}
   );
   console.log(result);
}

async function runModalFunction() {
  return new Promise(async (resolve, reject) => {
      await executeAsModal(() => {
     /*
    better to replace forEach with PromiseAll map or for circulate with await
   but for now I use forEach as a simple test
   */
          app.activeDocument.layers.forEach(layer => {
            if (layer.kind == "group") {
              layer.layers.forEach(camada => {
                if (camada.name === "CyanColor") {
                  (async () => {
                    console.log("actioned!!");
                    await actionCommands(camada.id);
                  })();
                  resolve();
                }
              })
            }
          });
   }, {"commandName": "Action Commands"});
  });
}

document.getElementById("btn").addEventListener("click", async () => {
  await runModalFunction();
});
2 Likes

Hi, @shuji,thank you for your effort, unfortunately I couldn’t make your script work with my plugin, which consists of changing the values of the “CyanColor” adjustment layer using the slider, with the group as the active layer.

js: where am I going wrong?

document.querySelector("#sld1").addEventListener("change", async (func) => {
	await core.executeAsModal(async () => {
			try {
				document.querySelector("#vsld1").innerHTML = func.target.value // alert(func.target.value)
				await runModalFunction()

			} catch (err) {alert(err)}
	},{"interactive": true})


}) 
//
//alert(document.querySelector("#vsld1").value)


async function actionCommands(id) {
	console.log(id);
	 const result = await batchPlay(
		[
		   {
			  _obj: "set",
			   _target: [// focus the layer following the id
				 {
					_ref: "adjustmentLayer",
					_id: id,
					_value: "targetEnum"
				 }
			  ],
			  to: {
				 _obj: "colorBalance",
				 midtoneLevels: [// color balance value
				    func.target.value,
					35,
					80
				 ]
			  },
			  _options: {
				 dialogOptions: "dontDisplay"
			  }
		   }
		],
		{}
	 );
	 console.log(result);
  }
  
  async function runModalFunction() {
	return new Promise(async (resolve, reject) => {
		await executeAsModal(() => {
	   /*
	  better to replace forEach with PromiseAll map or for circulate with await
	 but for now I use forEach as a simple test
	 */
			app.activeDocument.layers.forEach(layer => {
			  if (layer.kind == "group") {
				layer.layers.forEach(camada => {
				  if (camada.name === "CyanColor") {
					(async () => {
					  console.log("actioned!!");
					  await actionCommands(camada.id);
					})();
					resolve();
				  }
				})
			  }
			});
	 }, {"commandName": "Action Commands"});
	});
  }

Plugin:

if my code itself works correctly, I suppose the problem comes from function scope.
the value “func.target,value” from addEventListener socpe but it doesn’t exist in actionCommonds scope.
please inspect the value again.

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="style.css">   
</head>
<body>
  <sp-heading>Layers</sp-heading>
  <sp-body id="layers">
    No layers
  </sp-body>
  <!-- this is the slide bar I think you want to use -->
  <input type="range" id="rangebar" name="cowbell" min="0" max="100" value="90" step="10" />
  <span id="rangevalue"></span>
  <!-- -->
  <footer>
    <sp-button id="btnPopulate">Populate Layers</sp-button>
    <sp-button id="btn">color balance</sp-button>
  </footer>


<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
    viewBox="0 0 400 400"
>
  <rect x="0" y="10" width="100" height="200" class="rect" />
</svg>

<script src="main.js"></script> 
</body>

</html>

// Events recognized as notifiers are not re-playable in most of the cases. There is high chance that generated code won't work.
const { app } = require("photoshop");
const {executeAsModal} = require("photoshop").core;
const {batchPlay} = require("photoshop").action;

/**
 * @param id target layer id
 * @param barRange number value which is given in adjustment value.
 */
async function actionCommands(id, barRange) {
   const result = await batchPlay(
      [
         {
            _obj: "set",
             _target: [
               {
                  _ref: "adjustmentLayer",
                  _id: id,
                  _value: "targetEnum"
               }
            ],
            to: {
               _obj: "colorBalance",
               midtoneLevels: [
                  barRange,// even you can pass the value directly like document.getElementById("rangebar").value
                  35,
                  80
               ]
            },
            _options: {
               dialogOptions: "dontDisplay"
            }
         }
      ],
      {}
   );
   console.log(result);
}

const init = () => {
  document.getElementById("rangevalue").textContent = `range value ${document.getElementById("rangebar").value}`;
}

/**
 * @param barRange number value from top scope
 */
async function runModalFunction(barRange) {
  return new Promise(async (resolve, reject) => {
      await executeAsModal(() => {
          app.activeDocument.layers.forEach(layer => {
            if (layer.kind == "group") {
              layer.layers.forEach(camada => {
                if (camada.name === "CyanColor") {
                  (async () => {
                    console.log("actioned!!");
                    await actionCommands(camada.id, barRange);
                  })();
                  resolve();
                }
              })
            }
          });
   }, {"commandName": "Action Commands"});
  });
}

document.getElementById("btn").addEventListener("click", async () => {
const barRange = document.getElementById("rangebar").value;
  await runModalFunction(barRange);
});

// this is top scope
document.getElementById("rangebar").addEventListener("change", async (e) => {
  document.getElementById("rangevalue").textContent = `range value ${e.target.value}`;
  await runModalFunction(e.target.value);// be sure the range value is given in runModalFunction
});

init();

and this code quite mess because I just tried simple test.
even if the code works , you should sort it.

@AutomatePS, @shuji, i did some tests here, it’s simpler than I thought.
To change adjustment layer values in your plugin use this:

const sld1 = document.querySelector("#sld1");
sld1.addEventListener("change", async (func) => {
	document.querySelector("#vsld1").innerHTML =  sld1.value 
	await ajusteColor(sld1.value);
}) 
///////////////////////////
async function ajusteColor(c){
	const {executeAsModal} = require("photoshop").core;
	const {batchPlay} = require("photoshop").action;
	async function colorBalance() {
		await batchPlay( [ { _obj: "set", _target: [ { _ref: "adjustmentLayer", _name: "CyanColor", _value: "targetEnum" } ], to: { _obj: "colorBalance", midtoneLevels: [ c, 0,0] }, _options: { dialogOptions: "dontDisplay" } } ], { modalBehavior: "wait" } );
	}
	async function runModalFunction() {await executeAsModal(colorBalance);}
	await runModalFunction();
}
2 Likes

@PS-fxrios , it worked! That was all I needed. Thanks for sharing :grinning: