Stacking All Open Documents As Layers & Renaming Layers

@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!

1 Like