Move Layer to Front through batch-play works differently in PS 23.4.1

Hi @simonhenke,

Thank you so much for your help, I really appreciate it!

What exactly is not working? Do you get an error, unexpected behavior or is simply nothing happening?

Unfortunately nothing happened. I didn’t get an error in console as well.
I’m not a developer so I’m doing many things by tests and tries but I’m lacking a lot of basic knowledge :frowning: I’m trying to constantly learn new things and this community (including you) is helping me al lot.
In this case I suppose that it could be a matter of my mistakes in merging my function with @Karmalakas code.
I’ve noticed that we’re calling executeAsModal in different way.

I’m using

await core.executeAsModal(() => {
batchPlay(
            [

and @Karmalakas :

executeAsModal(async () => {
    batchPlay(

I’m also not using this part in my descriptors:

 synchronousExecution: true,
 modalBehavior: 'execute'

Is this the difference in API / manifest version or these methods can be used interchangeably?

I’ve tried to split my batchplay into two and to place @Karmalakas code in the middle like shown below but it didn’t work.


async function dustAndScratches() {

    const docexists = () => {
        return Boolean(app.documents?.length)
      }
      const exists = docexists()
     
      
if (exists === true) {

    await core.executeAsModal(() => {

        batchPlay(
            [{
                    _obj: "make",
                    _target: {
                        _ref: "layer"
                    },
                    layerID: 222,
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                }
            ], {
                "historyStateInfo": {
                    "name": "Dust and Sctartches pt1",
                    "target": {
                        "_ref": "document",
                        "_enum": "ordinal",
                        "_value": "targetEnum"
                    }
                }
            });
// added code


// I guess that this line was missing (you probably had it specified globally):
const doc = app.activeDocument;

// Get top-most layer
const topLayer = doc.layers[0]

// Get index of that layer
const topIndex = batchPlay(
    {
        _obj: 'get',
        _target: [
            {_property: "itemIndex"},
            {_ref: 'layer', _id: topLayer.id},
            {_ref: 'document', _id: docId}
        ],
        _options: {dialogOptions: 'dontDisplay'}
    },
    {
        synchronousExecution: true,
        modalBehavior: 'execute'
    }
)[0].itemIndex

// Move other layer to that index
executeAsModal(async () => {
    batchPlay(
        {
            _obj: 'move',
            _target: [{_ref: 'layer', _enum: 'ordinal', _value: 'targetEnum'}],
            to: {_ref: 'layer', _index: topIndex},
            adjustment: false
        },
        {
            synchronousExecution: true,
            modalBehavior: 'execute'
        }
    )
    // you had here:
    //  }, {"Command name"})
    // but I was getting an error - ':' expected. so I've replaced it with:
    }, {
    "historyStateInfo": {
        "name": "Dust and Sctartches pt2",
        "target": {
            "_ref": "document",
            "_enum": "ordinal",
            "_value": "targetEnum"
        }
    }
        })


// end of added code

            batchPlay(
                [
                {
                    _obj: "move",
                    _target: {
                        _ref: "layer",
                        _enum: "ordinal",
                        _value: "targetEnum"
                    },
                    to: {
                        _ref: "layer",
                        _enum: "ordinal",
                        _value: "front"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "move",
                    _target: {
                        _ref: "layer",
                        _enum: "ordinal",
                        _value: "targetEnum"
                    },
                    to: {
                        _ref: "layer",
                        _enum: "ordinal",
                        _value: "front"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "mergeVisible",
                    duplicate: true,
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },

                {
                    _obj: "set",
                    _target: {
                        _ref: "layer",
                        _enum: "ordinal",
                        _value: "targetEnum"
                    },
                    to: {
                        _obj: "layer",
                        name: "Dust and Scratches"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "select",
                    _target: {
                        _ref: "menuItemClass",
                        _enum: "menuItemType",
                        _value: "view200Percent"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "dustAndScratches",
                    _options: {
                        dialogOptions: "display"
                    }
                },
                {
                    _obj: "make",
                    new: {
                        _class: "channel"
                    },
                    at: {
                        _ref: "channel",
                        _enum: "channel",
                        _value: "mask"
                    },
                    using: {
                        _enum: "userMaskEnabled",
                        _value: "revealAll"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "invert",
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "select",
                    _target: {
                        _ref: "channel",
                        _enum: "channel",
                        _value: "mask"
                    },
                    makeVisible: false,
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "select",
                    _target: {
                        _ref: "paintbrushTool"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "reset",
                    _target: {
                        _ref: "color",
                        _property: "colors"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "select",
                    _target: {
                        _ref: "menuItemClass",
                        _enum: "menuItemType",
                        _value: "fitOnScreen"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                }
            ], {
                "historyStateInfo": {
                    "name": "Dust and Sctartches pt3",
                    "target": {
                        "_ref": "document",
                        "_enum": "ordinal",
                        "_value": "targetEnum"
                    }
                }
            });
    })
}
else {  PhotoshopCore.showAlert({message: 'Open the document first'});}
}
document.querySelector("#btndustAndScratches").addEventListener("click", dustAndScratches);

Your suggestion on history suspension is great, I’ll have to test it as well. It’s a shame though that each BatchPlay instance within one executeAsModal needs to have own history state.

Thanks!

I can confirm 23.4.1 broke BP behavior with multiple descriptors. This completely broke my plugin. It seems even defining synchronousExecution: true performs commands defined in a single descriptors array asynchronously. Looks like some descriptors defined at the end finish before the ones defined at the beginning.

I have a button with basically 3 descriptors in BP:

[
    {
        "_obj": "select",
        "_target": [
            {
                "_ref": "layer",
                "_id": 4
            }
        ],
        "makeVisible": false
    },
    {
        "_obj": "make",
        "new": {
            "_class": "channel"
        },
        "at": {
            "_ref": "channel",
            "_enum": "channel",
            "_value": "mask"
        },
        "using": {
            "_enum": "userMaskEnabled",
            "_value": "revealAll"
        }
    },
    {
        "_obj": "select",
        "_target": [
            {
                "_ref": "layer",
                "_id": 4
            }
        ],
        "makeVisible": false
    }
]

And it’s behavior now is completely random. Most of the time I get:

The command “Make” is not currently available.

But sometimes I get mask created. All I need to do is just keep clicking the button (maybe 1/20 times it works, maybe even less)… So in this case it looks like it tries to create a mask while layer is still not selected

IMO it’s another new bug

Ping @kerrishotts

Same happens on 23.5.0

My plugin is fully broken too :frowning: I’m trying to find the solution but without luck, no matter what I do, I’m getting errors.

Whenever you’re using await in a function, that function has to be async.

You don’t need the modalBehvaior property, it’s default value has always worked fine for me.
If you have synchronousExecution:true, you don’t need await.

It doesn’t, check my reply again.

Interesting, I also updated to 23.4.1 recently before migrating my next plugin and haven’t had any issues so far, although I have batchPlays with ~50-100 descriptors. Maybe it’s a specific event that’s causing the issue?

I’ll run your code example later to see if I get the same results.

I try to debug and see what’s happening, but can’t figure out. I changed button code to split commands for BP, but no luck. Basically I put this into my plugin:

require('photoshop').core.executeAsModal(async () => {
 let result = require("photoshop").action.batchPlay(
  [
   {
    "_obj": "select",
    "_target": [
        {
            "_ref": "layer",
            "_id": 2
        }
    ],
    "makeVisible": false
   }
  ], {
        synchronousExecution: true,
        modalBehavior: "execute"
    });
  console.log("BP1", result)

  result = require("photoshop").action.batchPlay(
  [
   {
    "_obj": "make",
    "new": {
        "_class": "channel"
    },
    "at": {
        "_ref": "channel",
        "_enum": "channel",
        "_value": "mask"
    },
    "using": {
        "_enum": "userMaskEnabled",
        "_value": "revealAll"
    }
   }
  ], {
        synchronousExecution: true,
        modalBehavior: "execute"
    });
  console.log("BP2", result)
}, {"commandName": "test"})

And it works fine. But if I do pretty much the same while clicking a button, I get

The command “Make” is not currently available.

Will continue debugging to figure what’s preventing creating a mask (I have more actions happening before mask creation, but don’t see how they can impact the command). I’m really pissed that almost every Ps update breaks something

So no matter how I try, I still get that command is not available…
Here are my descriptors:

[
    {
        "_obj": "select",
        "_target": [{"_ref": "layer", "_id": 2}],
        "makeVisible": false
    },
    {
        "_obj": "make",
        "new": {"_class": "channel"},
        "at": {"_ref": "channel", "_enum": "channel", "_value": "mask"},
        "using": {"_enum": "userMaskEnabled", "_value": "revealAll"}
    },
    {
        "_obj": "select",
        "_target": [{"_ref": "layer", "_id": 3}],
        "makeVisible": false
    },
    {
        "_obj": "make",
        "new": {"_class": "channel"},
        "at": {"_ref": "channel", "_enum": "channel", "_value": "mask"},
        "using": {"_enum": "userMaskEnabled", "_value": "revealAll"}
    },
    {
        "_obj": "select",
        "_target": [{"_ref": "layer", "_id": 2}],
        "makeVisible": false
    },
    {
        "_obj": "select",
        "_target": [{"_ref": "layer", "_id": 3}],
        "makeVisible": false,
        "selectionModifier": {
            "_enum": "selectionModifierType",
            "_value": "addToSelection"
        }
    }
]

No matter if I execute them in a single BP or one by one I get the responses for them:

[
    {},
    {
        "_obj": "error",
        "message": "The command “Make” is not currently available.",
        "result": -25920,
        "failedDescriptor": {
            "_obj": "make",
            "new": {"_class": "channel"},
            "at": {"_ref": "channel", "_enum": "channel", "_value": "mask"},
            "using": {"_enum": "userMaskEnabled", "_value": "revealAll"}
        }
    },
    {},
    {
        "_obj": "error",
        "message": "The command “Make” is not currently available.",
        "result": -25920,
        "failedDescriptor": {
            "_obj": "make",
            "new": {"_class": "channel"},
            "at": {"_ref": "channel", "_enum": "channel", "_value": "mask"},
            "using": {"_enum": "userMaskEnabled", "_value": "revealAll"}
        }
    },
    {},
    {}
]

Have to note, that none of the layers have any masks while trying to create one. Also, as mentioned previously, very very rarely it works as expected - masks are created (both in a single BP and in a separate calls)

Win10
Ps 23.4.1
API v2
Manifest v4

I have same issue with mask creation.

So to sum up, two issues I’ve noticed:

  • Moving layers through BP works differently. (error popups when the layer is already on top when I’m trying to move it up. It didn’t before)
    {
                    _obj: "move",
                    _target: {
                        _ref: "layer",
                        _enum: "ordinal",
                        _value: "targetEnum"
                    },
                    to: {
                        _ref: "layer",
                        _enum: "ordinal",
                        _value: "front"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                }
  • Error when I’m trying to create mask The command “Make” is not currently available.
        {
                    _obj: "make",
                    new: {
                        _class: "channel"
                    },
                    at: {
                        _ref: "channel",
                        _enum: "channel",
                        _value: "mask"
                    },
                    using: {
                        _enum: "userMaskEnabled",
                        _value: "revealAll"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                }

This is the whole code that throws me an error during mask creation (Mac, PS 23.4.1, Api 2.0 Manifest version 5)



async function dustAndScratches() {

    const docexists = () => {
        return Boolean(app.documents?.length)
      }
      const exists = docexists()
     
      
if (exists === true) {


    await core.executeAsModal(() => {
    

        batchPlay(
            [
                {
                    "_obj": "select",
                    "_target": {
                        "_ref": "layer",
                        "_id": app.activeDocument.layers[0]._id
                    },
                    "_isCommand": true,
                    "_options": {}
                },
                {
                    _obj: "make",
                    _target: {
                        _ref: "layer"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "mergeVisible",
                    duplicate: true,
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },

                {
                    _obj: "set",
                    _target: {
                        _ref: "layer",
                        _enum: "ordinal",
                        _value: "targetEnum"
                    },
                    to: {
                        _obj: "layer",
                        name: "Dust and Scratches"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "select",
                    _target: {
                        _ref: "menuItemClass",
                        _enum: "menuItemType",
                        _value: "view200Percent"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "dustAndScratches",
                    _options: {
                        dialogOptions: "display"
                    }
                },
                {
                    _obj: "make",
                    new: {
                        _class: "channel"
                    },
                    at: {
                        _ref: "channel",
                        _enum: "channel",
                        _value: "mask"
                    },
                    using: {
                        _enum: "userMaskEnabled",
                        _value: "revealAll"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "invert",
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "select",
                    _target: {
                        _ref: "channel",
                        _enum: "channel",
                        _value: "mask"
                    },
                    makeVisible: false,
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "select",
                    _target: {
                        _ref: "paintbrushTool"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "reset",
                    _target: {
                        _ref: "color",
                        _property: "colors"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                },
                {
                    _obj: "select",
                    _target: {
                        _ref: "menuItemClass",
                        _enum: "menuItemType",
                        _value: "fitOnScreen"
                    },
                    _options: {
                        dialogOptions: "dontDisplay"
                    }
                }
            ], {
                "historyStateInfo": {
                    "name": "Dust and Sctartches",
                    "target": {
                        "_ref": "document",
                        "_enum": "ordinal",
                        "_value": "targetEnum"
                    }
                }
            });
    })
}
else {  PhotoshopCore.showAlert({message: 'Open the document first'});}
}
document.querySelector("#btndustAndScratches").addEventListener("click", dustAndScratches);

Works fine for me in 10 out of 10 tries :thinking:
I’m on manifest version 4, maybe 5 is causing the issue.

Can’t test it right now as I’m getting
image
when changing to version 5. Not sure where in my project any code generation from strings might be, at least I’m not aware of any.

I’m still on v4 also


Some raw onclick maybe somewhere on a pure HTML element? :slight_smile:


Also if I have masks on these two layers and change commands from make to delete, they work just fine and masks are deleted

Also if I have masks on these two layers and change commands from make to delete , they work just fine and masks are deleted

I wonder wow the Photoshop Action would work comparing to script. (if it would give an error too).
I’ve tested action regarding moving layers and it was also not working on 23.4.1 but did on previous versions.

I’ve noticed interesting thing - Mask creation doesn’t work if it’s done on top layer. Id does work if there is another layer above :confused:

EDIT: this doesn’t apply to all functions which is even more confusing…

For me it doesn’t work either way

nope, I only have React components.
Also, I commented out my whole index.tsx and the error still occurs, maybe it’s related to some webpack or babel code :confused: So I guess I can’t move to manifest v.5 :sweat_smile:

@kerrishotts, could you please take a look on this? What was changed in 23.4.1 and how can we deal with that now?

out of curiosity I tried your code on my plugin which in API 2 Manifest 5, Photoshop 23.4.1 it worked well:
here is your whole code with what I changed

const ExecuteAsModal = require("photoshop").core.executeAsModal;

document
  .getElementById("btndustAndScratches")
  .addEventListener("click", async function () {
    await ExecuteAsModal(() => dustAndScratches());
  });
async function dustAndScratches() {
  const docexists = () => {
    return Boolean(app.documents?.length);
  };
  const exists = docexists();

  if (exists === true) {
    await batchPlay(
      [
        {
          _obj: "select",
          _target: {
            _ref: "layer",
            _id: app.activeDocument.layers[0]._id,
          },
          _isCommand: true,
          _options: {},
        },
        {
          _obj: "make",
          _target: {
            _ref: "layer",
          },
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
        {
          _obj: "mergeVisible",
          duplicate: true,
          _options: {
            dialogOptions: "dontDisplay",
          },
        },

        {
          _obj: "set",
          _target: {
            _ref: "layer",
            _enum: "ordinal",
            _value: "targetEnum",
          },
          to: {
            _obj: "layer",
            name: "Dust and Scratches",
          },
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
        {
          _obj: "select",
          _target: {
            _ref: "menuItemClass",
            _enum: "menuItemType",
            _value: "view200Percent",
          },
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
        {
          _obj: "dustAndScratches",
          _options: {
            dialogOptions: "display",
          },
        },
        {
          _obj: "make",
          new: {
            _class: "channel",
          },
          at: {
            _ref: "channel",
            _enum: "channel",
            _value: "mask",
          },
          using: {
            _enum: "userMaskEnabled",
            _value: "revealAll",
          },
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
        {
          _obj: "invert",
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
        {
          _obj: "select",
          _target: {
            _ref: "channel",
            _enum: "channel",
            _value: "mask",
          },
          makeVisible: false,
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
        {
          _obj: "select",
          _target: {
            _ref: "paintbrushTool",
          },
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
        {
          _obj: "reset",
          _target: {
            _ref: "color",
            _property: "colors",
          },
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
        {
          _obj: "select",
          _target: {
            _ref: "menuItemClass",
            _enum: "menuItemType",
            _value: "fitOnScreen",
          },
          _options: {
            dialogOptions: "dontDisplay",
          },
        },
      ],
      {
        historyStateInfo: {
          name: "Dust and Sctartches",
          target: {
            _ref: "document",
            _enum: "ordinal",
            _value: "targetEnum",
          },
        },
      }
    );
  } else {
    PhotoshopCore.showAlert({ message: "Open the document first" });
  }
}

edited to add missing Variable

Hi @Wanokuni

interesting, when I’m using your version of the function, nothing happens after clicking the button. I’m looking for a reason now.

I forgot to add this line:

const ExecuteAsModal = require("photoshop").core.executeAsModal;

as I have those variables declared at top in the index file, that’s why. could you add it and check again if it works.

Check for inline handlers – or, if you’re using webpack, check how your sourcemaps are being handled – default is to use eval – which will cause the issue.

If you’re using webpack, you can (for dev purposes), add allowCodeGenerationFromStrings: true to your requiredPermissions section in the manifest, and the error should go away.

Downgraded Ps to v23.3.2 (don’t have v23.4.0 on the list of Other versions) and it works perfectly

@kerrishotts, could you please ping someone? This really needs to be fixed (or at least some workaround given). As mentioned, same issue on v23.5 beta

@heewoo @samgannaway FYI ^^^^

1 Like