Question about migrating CEP plugin to UXP

Hi there,
I have a CEP Photoshop plugins with different Extensions, each extension has its html file and script file and when using the plugin, I need to open and close different extensions to execute different tasks like user login and upload image to our server.
Currently we use csInterface.requestOpenExtension() and csInterface.closeExtension() to handle different extensions open and close, but I don’t see similar function in UXP. How should I do the same thing in UXP? I try to add each extension in UXP’s manifest as different panel, but seems there could be only one html file as the entry of the plugin in one manifest.

Thanks

Do you need multiple panels (interfaces) or multiple commands?
Either way, you’d have to declare both of these in the entrypoints of your manifest.json.

Then, in your js file, you need to call the setup function of the entrypoints object (require("uxp").entrypoints.setup(...)) to setup your commands and panels. As you’ve mentioned, there’s one main html file acting as your starting point. So for multiple panels, you’ll have to write some logic to dynamically switch out the view. I think some of the code examples also has multiple panels, not sure which though. You can search through here:
https://github.com/AdobeDocs/uxp-photoshop-plugin-samples/search?q=entrypoints.setup

Quote from the KitchenSink example:

// you’d normally attach your UI to a panel or create some dynamic elements here.
// but for a single panel with an index.html file, that’s not required.
// If you had multiple panels, though, you’d have to at least find the HTML node
// and attach it to the incoming node, otherwise the panels (other than the first)
// would show blank (and the first panel would likely include all your UI).

Hi Simon
thanks for your reply, I check the example of kitchenSink and seems I need to move all other html files to the index.html, and divide them as different tabs.
I see this in the Unsupported Attribute page: most event handlers should be attached using addEventListenerinstead of theiron* counterparts. But in kitchenSink it just use onClick. Would it be ok to use these event handlers in html elements or I should use addEventListener to config event handlers in js files.
Also I want to know is it possible to use axios in UXP?
Thanks

You don’t have to do move everything into one HTML file. There are many ways to include parts of your HTML in other files, mainly various templating libraries. I personally recommend React (which is more than just a templating library of course), since UXP is build to work nicely with React - and well, React is awesome.
You can also use plain javascript to dynamically attach HTML to the DOM. That being said, you also don’t have to show your different content as tabs (inside a panel). I personally moved away from showing too much content in one panel (via tabs), instead I prefer providing multiple panels as I described in my previous post. After all, panels are already tabs itself if you consider the whole Photoshop interface.

Not sure, maybe it’s just the onClick that’s working. I’d just try it out and see what’s working. With React you can attach event listeners directly to the elements via on*EventName*

Hello, related with this I need to know how to make specific “csInterface.requestOpenExtension()”. I want to open a uxp plugin (is hide) from another uxp plugin (for example, clicking in a button) that is in the same entrypoint. How can I do this? Thanks

It’s possible, yet a little complicated:

  1. Navigate Photoshop’s menu to match the plugin by name, then the panel by name.
  2. Read out the apps property panelList to check if the panel is currently visible
  3. Programmatically open the panel by its menu command

To save you some time:
(remove types if you’re not using Typescript)

export const showPluginPanel = (pluginName: string, panelName: string, closeIfOpen: boolean = false) => {
  const menuBar = getAppProperty('menuBarInfo')
  const plugins = menuBar.submenu.find(item => item.menuID === 7200)
  const plugin = plugins.submenu.find(item => item.title === pluginName)
  const panel = plugin.submenu.find(item => item.title === panelName)
  const panelState = getAppProperty('panelList').find(p => p.name === panelName)
  if (!panelState.visible || panelState.obscured || closeIfOpen) {
    performMenuCommand(panel.command)
  }
}

export const appRef = { _ref: 'application', _enum: 'ordinal', _value: 'targetEnum' }

export function getAppProperty<T extends keyof ApplicationDescriptor>(prop: T): ApplicationDescriptor[T] {
  return photoshop.action.batchPlay([
    {
      _obj: 'get',
      _target: [{ _property: prop }, appRef],
    },
  ], { synchronousExecution: true })[0][prop]
}

export function performMenuCommand(commandID: number) {
  photoshop.core.performMenuCommand({
    commandID
  });
}
2 Likes

It works perfectly! Thank you!