Adobe UXP: Things you need to know! #4 Commands, Panels and the manifest.json

In this episode I’m discussing Panels vs. Commands in Vanilla JS UXP plugins and the way they are expressed in the manifest.json

If you find this content useful, please consider supporting me – 2020 is a hell of a year. I don’t have a Patreon page, but I’ve got two fairly cheap plugins on the Photoshop Marketplace, ALCE (Advanced Local Contrast Enhancer) and Double USM (on sharpening). If you happen to buy them, please leave a positive rating/review, it would greatly help. Or, you can

If you cannot, or don’t want to, that’s OK anyway.
Stay safe and thanks! 🙏🏻

The whole series so far


The transcripted video looks weird, but I’m told it helps to have it anyway because it’s easier to translate to other languages. Apologies for the lack of punctuation and the sloppy syntax.

Hey I’m Davide and this is Adobe UXP things you need to know! Today I’m talking about Panels versus Commands in vanilla JavaScript plugins and the way they are expressed in the manifest.json. Plus some other stuff, but this is the main plan so let’s get to it. I need some plug-in samples to work on so head to this GitHub repo and grab them all. The ones that I’m interested into this time are the direct-action-js-sample the hello-world-panel-js-sample and the ui-kitchen-sink sample so let me load in Photoshop as well this first one here so back to the UXP DevTool and if you are unsure how to operate with the UXP DevTool please refer to my previous video let’s add this one first so let’s point to the manifest.json which is here and open this loads it not in Photoshop but in the ui of the UXP DevTool and then with this load command we should find it in Photoshop unless we don’t and the reason why we don’t see anything is because this plugin isn’t made of panels if you remember something that I told you I think in the very first video is that UXP plugins could be made of zero or more panels zero or more commands zero or more modal dialogues and this one has no graphic user interface has no panel it’s just one command in fact you can find it here in the plugins sample write layer names and this is the commands or also in the plugins panel and here you find it right so let’s see what this is all about so let me try to run it first to see that it is operational please open at least one document let me create quickly one then duplicate the background layer a bunch of times click write layer names to a file and this pops up this dialog that asks me to save this layer names txt let’s pick UXP and then samples this is a good place I think it’s done so let’s check in the Finder we should have it UXP samples and here it is layernames.txt and here are the layer names that I have in my document. So cool this works. Now I’m not really interested into the mechanics of the plugin but in the way that it is structured to contain a single command or more commands as we will see because you have to know, and this is a very important take away, that in UXP there is no concept of an independent script. At least so far. So the things that you were used to find for instance in the file > scripts > browse… menu so loading and running a JSX file, a single independent script this is not any more a thing in UXP again at least so far so things might change in the future but right now scripts can live only within the context of a UXP plugin which is exactly what this sample write layer names plugin does: it contains one command aka one script so let’s look at the code for it and the one thing that I want to show you first is the manifest.json so let me collapse everything and here you have the id which in this case follows the reverse url convention so it’s com.adobe. and then the name of the panel instead of the alphanumeric string that you are given by the UXP marketplace actually by the developer console when you want to create a new product for the UXP marketplace you could add here everything even mamma mia that’s going to work just fine then you have the name which is the string here the version 1.0.0 and the entry point which in this case is a JavaScript file and this makes sense because this plugin doesn’t have a graphic user interface so you don’t have the need for an html file. Usually plugins with panels have an entry point of an html index.html in this case it makes sense to have a JavaScript file. In case of React it makes no difference you can have either the JS or the html in case of the JS it’s React that is injecting the html elements in the DOM via JavaScript right so then the host array we have Photoshop minimum version 22.0.0 and the manifest version must be at least four now let me skate over the entry points for the moment and let’s focus on the icons so this is an array but it contains only one object because this icon that we are specifying here with this path applies to all the themes the darkest dark the lightest and the light medium I am positive is just an Adobe XD thing so you can remove it and all is a bit redundant here if you want to have an icon for the dark theme set and another for the light theme sets you just have to duplicate this entire object and of course pick the right icon and then set the themes for which it applies very important the species here: is pluginList and this pluginList means the icon for Photoshop’s own plugins panel which is different in case of a panel that has its own icon but we will see that when we deal with panels in the next example so these are the icons and please note that even if we specify this plugin-icon.png we don’t have it in the icons folder we have plugin-icon@1x and plugin-icon@2x this is because we have a couple of scales the default one and the one for retina displays and we specify explicitly the size for the retina one here so the 2x is 48 x 48 and the 1x is 24 x 24 and we add the 48 here okay so these are the icons let’s now focus on the entry points this is an array because you can have more than one we have just one command here which is this one write layer names to a file so one object the type is command instead of panel and you have an id of writelayers and this is really important so remember that and then a label: label is of course what is written here write layer names to a file and it has default property because you can localize this so for instance if you want to add an Italian version you say “it” and then you add here the translation okay but you must provide the defaults let me get rid of that and be aware that trailing commas are evil in manifests so they’re going to break everything if you leave them around so let’s save back the file as it was all right so the id I said is really important so let me close those icons and let me open the index.js let’s start from the very beginning so you are requiring UXP and UXP is provided by the environment and if you are unfamiliar with this kind of syntax which is modern JavaScript let’s say that this is equal to const UXP is equal to require UXP so you’re saying listen: require UXP returns an object and this object has an entrypoints property so you could say UXP dot entry point and that would be perfectly fine you could even say listen let’s stick that into another constant so const entrypoints let me grab this here equal UXP dot entrypoints right with this modern syntax you are saying I know I understand that you’re returning me an object with an entrypoints property and I’m only interested into that so please stick it into an entrypoints constant okay right so the entrypoints has a setup method and the setup method accepts an object which is this one this object has a commands property which is in turn another object and in here you can have all the commands that your UXP plug-in is going to host right in this case it’s only one because we have only one so this command here and this property here must match absolutely with the id that you set in the manifest json so think about it this way the manifest.json tells Photoshop how to construct the UXP plug-in so what are the elements you have one UXP plugin named this way that contains for instance one command with this name but it’s the index.js that links that menu item with the function that you’re going to run so this write layers commands is going to run this write layers function which is defined below so let me collapse all those because we’re not really interested in what they are doing but the way they are connected to the manifest again if you are unfamiliar with the syntax well this is just an anonymous function you could write it like a function and then instead of the fat arrow you add parenthesis here okay so it’s an anonymous function that runs a write layers this is exactly the same actually there is no need to wrap it with an anonymous function because you run just this so you could just say you write layers write layers without the parenthesis okay actually even better if this was a lowercase L so let me quickly rename this this way there is no need to duplicate the writing you could just say write layers stop right and this works so let me save this just to show you that it works this is being loaded I want to watch it it’s watching for changes I’m saving so this should be it and you see that it pops up the dialog as it is supposed to do so this is a perfectly viable syntax but let’s get back to the original one all right so what does it say here if we had other menu items it would go here and then the manifest.json file so let’s try to do that let’s add another command so let me duplicate the entire object because we are developers we love to copy and paste and instead of write layers this is going to be write I don’t know what documents okay and the label is write document names to a file like that so in this case we need to add another command so comma and write documents and another anonymous function that well let me do something different since we have a show alert here which is just a wrapper for the Photoshop app show alert function let me alert something like I don’t know what writing docs okay so at this point we have this command that matches this id, does it? It does, so let me save everything. I know for a fact that watching for changes at least right now doesn’t take into account manifest changes so if you see here you find just one item in the plugins menu as well so you need to explicitly load it again plug in loaded successfully and now you have both of them and let’s click write document names to a file and an alert should pop up and sure enough it does so it works so you have seen how to add another commands how to create plugins with a command just in case again you’re not really familiar with this new syntax let me tell you how to compartmentalize – is that a word? – how to split the code into a different file so we have all these functions here that you might want to have in another separate file so let me create one and I’m calling this I don’t know what lib.js and let me paste everything in here it’s not too big of a deal I mean there are just three functions but functions grow here we need the right layers and the show alerts so we need to export those in the lib.js so let me module dot exports

and then I want the write layers to disk which is equal to write layer to disk and then duplicate this and then do the same with show alert which is here let me save this and in the index.js I need to do something quite close to that so instead of entrypoints and requiring UXP we are requiring the lib that sits in the same folder as this index.js and we are interested in the show alert and in the write layers

with a capital L so this should work fine we are watching for changes and if I click this the alert pops up and if I click the other it doesn’t so something is missing let me try to understand what write layers

oh write layers to disk I took the wrong one so write layers is the one that I need to export and it has a lowercase L so let me save this and rename that as well and I think that should be it so write layer names to a file still doesn’t work okay let’s take a chance and debug that so debug type error write layers is not defined with a capital l so probably again I’m messing with the yeah messing with the with the names oh because I renamed that oh my bad to show you that you could just remove the second part of the assignment in the object that was a capital L write layers with a capital L and again capital L here and here save capital L save again and this time I bet that is going to run please yes it does so it was little error on my side apologies for the time spent all right so that was it for the commands plugin let’s see what is in a panels plugin so the hello-world-panel-js-sample I want to add the plugin which is this panel-js-sample here so let me point to the manifest json and also load it in Photoshop loaded it successfully and this time it has a UI so it’s just a panel and you see it here sample hello world panel right you can close it and open it this way or from the plugin menu hello world sample what can we do we can show alerts uh we can populate the layers very much like the other one but this time in the panel itself we saw that code in a previous video and we can also clear the list so let’s see what’s different in terms of manifest and the JavaScript code so same things as long as the id name version are involved the main entry point is different though we have an index.html again because this is a panel and it makes sense to have an html the host is the same manifest version as well the icons are the same yes exactly the same the difference is in the entry points for once we have a type of panel this time an id of hello world the id is really important we will see why in a moment then we have a bunch of sizes minimum size maximum size that are exactly what you are expecting them to be and also a preferred docked size so when the panel is docked like this this is a preferred size and the preferred floating size so when it is floating freely all right then you have the label again with the default hellworld sample and the icon so this is different compared to those icons so the icons that are outside of everything are the icons for the plugins panel whereas the icons that are inside the entry point in this case are the panels icons so these icons here you see that this is pretty much different compared to that another difference for the icon is as follows so you still have the theme and in this case you see that you have two different objects in the icons array one for the darkest and dark and another for the lightest and light but also you specify the width and the height of the default scale not the retina scale so on the one hand these are different so it’s 23 times 23 and 46 times 46 instead of the plugins icons that are 24 and 48 so few pixels of difference but here you set the standard slash

default size whereas in the plugins panel you set the retina size right so this is a little bit of a difference that you have to be aware of when writing your manifest otherwise you’re going to run into unwanted behaviours so let’s have a look at the JavaScript side so this is the manifest we expect to see something similar to this entry point setup thing here and we don’t because apparently you can get away with not having an entry point set up if it’s not strictly needed so here you don’t have it but the panel works fine just the same I don’t know if they are going to require that in the future but so far that’s a possibility but let’s look at the kitchen sink for comparison so in this case the kitchen sink let me reload that once more we saw that in a couple of previous videos ui-kitchen-sink it’s here manifest.json open and then also load it into Photoshop here it is let me close that white background that annoys me all right the kitchen sink is a particular panel in the sense that it contains both a panel which is this one and a command so both of them and you can see them better probably in the menu item the kitchen sink is the plugin and the reload plugin which in this case just reloads the plugin is a command so it contains both of the different types so you have two entry points one object here for the type command id reload plugin and another one for the type panel with id kitchensink and in the JavaScript you have this entrypoints and you should see them somewhere below let me try to find them entry points here you have the entry points setup function that has both the commands and the panels and this kitchen sink here is exactly the same id that is defined in the manifest so again think about the manifest as what to display in the Photoshop ui versus the JavaScript how to link this to actual functions and one that you see here in the panels is the show function that takes a funny argument but we won’t go into that in this video maybe in a later one so what happens in this show is code that is run the first time that the panels show in this case they’re using local storage so let me briefly explain it to you when you switch from tab to tab the active tab is saved in the local storage so that in the next session when you open the panel it opens showing the same exact tab that you left off with okay so this is one possibility let me try to implement the same kind of functionality in this hello-world-panel-js-sample so we need to import again the same let me copy and paste because I’m lazy the same entry point thing so here above we want to have the entry points and then we need to run the setup function in the entry point so entry points dot setup and this accepts an object that has a panels

property that in turn contains an object again that has this time the id the id of the panel and again be aware that we’re not talking about the idea of the plugin but the id of the panel which is hello world okay so this here and this one is going to have an object and in here we have a show function and in this show function what do I want to do I don’t know just maybe show an alert and show alert again is just a wrapper for the require Photoshop app show alert so show alerts and I don’t know showing like that okay so let me save am I watching this plugin I doubt in fact I’m not watching let me watch for changes and let me save again just to trigger the watch and it says showing now very important thing don’t think that this function shows every time that the panel is closed and reopened because it won’t let me close the kitchen sink the hello world sample is here you see it doesn’t and the reason why is that this shows only the first time because UXP panels are persistent by default if you have experience with CEP panels you know that the initialization code that they might contain would run each time that you reopen them unless they are persistent in which case the init code runs just once while UXP panels are persistent by default so no matter how many times that you close and reopen them this show will run just once if you want to run code based on the show hide status you have to follow a different path that we might look at in a future video so the bottom line is that this show alert just run once okay okay let me recap what we have seen so far we can have plugins that contain just commands scripts must be wrapped with UXP plugins and this is how to do it you can have more than one command inside the same UXP plugin and we’ve also seen how to export functions to be imported with this require syntax and the object destructuring syntax here then we have this hello world panel sample where instead we have panels as opposed to commands and this is defined by the type we still have the id and we can have this entrypoints set up panels and the show function again this must match the string that is found in the manifest.json right and then in the kitchen sync example we have seen a plugin that contains both a command and a panel and the way that the two are set up in the entrypoints setup right? OK I think this is it for this time I hope you have learned something new and if this is the case please consider  

supporting me I want to thank all the people who made a donation there’s a button on my website it’s been truly very much appreciated you can also purchase ALCE and DoubleUSM in the Photoshop marketplace and if you happen to buy them please leave a positive rating or a review which will always help very much as well. Okay thanks for watching and see you in the next one. Bye!