@RyanBlack no worries, if it weren’t for the kindness of random strangers on internet forums then I wouldn’t be here to help you now!
I think you are probably heading for a facepalm moment if you can already take Alchemist code and wire it up to a button, so hopefully the below will make things a bit clearer.
batchPlay and UXP are not independent of each other, batchPlay is part of UXP and is just one way of writing functions that directly control Photoshop; using the API is the other.
You can include batchPlay functions in your JS files alongside API calls, or just use API calls (provided they exist for the Photoshop actions you want to perform), or just batchPlay the whole thing. There’s no real advantage to either approach.
I’ve had a chance to test/tweak the code I posted last night (it was janky, in my defense, it was very late when I wrote it!), but I’ve got a working version for you. Let’s have a look at it and see if it helps you understand UXP a bit better…
Make yourself a new plugin with the UXP Developer Tool or grab one you already have and in your index.html
add:
<sp-button id="btnStack">Stack</sp-button>
I’m assuming you can figure that one out!
Next, bob over to index.js
and at the very top of the file you’re going to want to handle your imports.
For this we’re going to need app
and core
from the photoshop
module:
const { app, core } = require("photoshop");
This is going to load a whole load of JS from UXP into our index.js
file at compilation time that’ll give us access to the API functions associated with app
and core
as if we’d coded them into our file ourselves.
You’ll have done similar for batchPlay.
Next we’ll take a modified version of what I posted last night and wrap it in a function called stackLayers()
:
//running asynchronously because we want to await executeAsModal
const stackLayers = async () => {
// the doc to stack into
const docZero = app.documents[0];
// the other open docs
// this uses ES6 array.filter to return an array of the open documents that DO NOT share the same id as the first open document
const docsToStack = app.documents.filter((doc) => doc._id !== docZero._id);
// a counter to use for naming layers - declaring outside the scope of our for loop so as not reset on each iteration
let docCounter = 0;
// execute as modal is required for functions that change the state of Photoshop or documents
// think of it as a function that 'wraps' yours and tells Photoshop to go into a modal state and not allow anything to interrupt it from doing whatever is contained in the executeAsModal
// we also call it with the await keyword to tell JS that we want to wait for it to complete before moving on to later code (in this case there isn't any though)
await core.executeAsModal(() => {
// flatten docZero
docZero.flatten();
// rename layer with counter
docZero.layers[0].name = `Layer ${docCounter}`;
// increment counter
docCounter++;
// loop through other open docs
for (const doc of docsToStack) {
// flatten
doc.flatten();
// rename layer with counter
doc.layers[0].name = `Layer ${docCounter}`;
// increment counter
docCounter++;
// duplicate layer to docZero
doc.layers[0].duplicate(docZero);
// close doc
doc.closeWithoutSaving();
}
});
};
Finally, we need to add an eventListener to index.js
for the button that will call our function, we’ll put it right after the stackLayers() function we wrote:
document.getElementById("btnStack").addEventListener("click", stackLayers);
Bob’s your uncle, one pure API UXP plugin!
For completion’s sake here’s a rough example of including some batchPlay in our stackLayers() function:
const { app, core } = require("photoshop");
const batchPlay = require("photoshop").action.batchPlay;
// a batchPlay function to flatten active document
const flattenBatchPlay = () => {
return batchPlay(
[
{
"_obj": "flattenImage",
"_isCommand": true
}
],{});
}
// our old stackLayers() function
const stackLayers = async () => {
await core.executeAsModal(() => {
// call batchPlay function
flattenBatchPlay()
// some vanillla JS
docZero.layers[0].name = `Layer ${docCounter}`;
// do some stuff - batchPlay or API
// close doc with API
doc.closeWithoutSaving();
});
};
Hopefully that’s illuminated things a bit for you - otherwise feel free to DM me!