Skip to content

Commit

Permalink
Ps Action recording for UXP plugins and Core page update. (#438)
Browse files Browse the repository at this point in the history
* Add Action recording info for plugin call and menu enablement in manifest.

Reweight heading in batchPlay to enable right side nav anchors.
Add suppressProgressBar option to batchPlay with link from above.

* Clean up a few bits of documentation.

* Add more to Core module page
  • Loading branch information
samgannawayA authored Aug 9, 2023
1 parent a3d66a9 commit bee3ecf
Show file tree
Hide file tree
Showing 13 changed files with 380 additions and 41 deletions.
4 changes: 4 additions & 0 deletions reference-ps.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,10 @@ module.exports = [{
{
"title": "CSDK <-> UXP Messaging",
"path": "/ps_reference/media/cpp-pluginsdk/"
},
{
"title": "Action Recording",
"path": "/ps_reference/media/action-recording/"
}
]
},{
Expand Down
4 changes: 2 additions & 2 deletions src/pages/guides/uxp_guide/uxp-misc/manifest-v4/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ Key path | Type | Description | Required
`main` | `string` | Path to the your plugin initialization code. This can be a JavaScript file or an HTML file. | Optional (defaults to `main.js`)
`icons` | `IconDefinition[]` | Icons for your plugin (which may be rendered in various contexts, such as the plugin panel) <br/> PNG, JPG/JPEG formats are supported and the max file size for each icon is 1MB. <br/> You should specify at least the 1x and 2x size. Icons for the Plugin Manager are uploaded directly via the Adobe Developer Console, not included within your plugin itself. See our ["Publishing your plugin" guide](../../../distribution/packaging-your-plugin/) to learn more. | Publish
| `host` | `HostDefinition\|HostDefinition[]` | Describes the supported applications that can be used with this plugin. This can include the type of application, the minimum required version, or the maximum version of the host app that the plugin supports. <br/><br/> **Note:** An array can ONLY be used during development. A single definition will be needed when submitting to the marketplace | Develop / Publish
`entryPoints` | `EntryPointDefinition[]`| Describes the entries your plugin adds to the _Plugins_ menu & plugin panel. See the next section for details. | Develop / Publish
`entrypoints` | `EntryPointDefinition[]`| Describes the entries your plugin adds to the _Plugins_ menu & plugin panel. See the next section for details. | Develop / Publish

## Icons

Expand Down Expand Up @@ -116,7 +116,7 @@ Key | Type | Description | Required

## Entry Points

The `entryPoints` field is an _array_ of objects matching the `EntryPointDefinition` format specified below. These entries appear both in the _Plugins_ menu in the native menubar, and the plugin panel.
The `entrypoints` field is an _array_ of objects matching the `EntryPointDefinition` format specified below. These entries appear both in the _Plugins_ menu in the native menubar, and the plugin panel.

Each entry point specifies a `type`, to create either a direct-action command or a panel show/hide command.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ title: Photoshop Product specific manifest
description:
---

# Photoshop Product specific manifest
# Photoshop specific manifest properties

In addition to the common UXP manifest attributes, Photoshop has an additional key/value field `apiVersion`. This field is located under the `data` object inside the `host` value.
The following illustrates the use of the `apiVersion` field.
With Photoshop added as a "host" in the UXP plugin manifest, there are a few options available under the `data` object.
- apiVersion
- loadEvent
- enableMenuRecording

```json
{
Expand All @@ -31,9 +33,17 @@ The following illustrates the use of the `apiVersion` field.
"minVersion": "23.0.0",
"data" : {
"apiVersion": 2,
"loadEvent": "use"
}
"loadEvent": "use",
"enableMenuRecording": true
},
},
"entrypoints": [
{
"type": "command",
"id": "MENU_ITEM_ID",
"label": { "default": "menu item's text"}
}
]
}
```

Expand All @@ -47,7 +57,7 @@ It is these restrictions that guarantee against multiple plugins modifying Photo

In order to allow plugin developers to gradually move to the new model, Photoshop 2022 supports both the original and the new JavaScript modes. A plugin uses the apiVersion field to specify which model it uses.
* `apiVersion` of **1** signifies that the plugin is using the original Photoshop 2021 implementation.
* `apiVersion` of **2** signifies that the plugin is using the new modal JavaScript feature.
* `apiVersion` of **2** signifies that the plugin is using the new modal JavaScript paradigm.

With the introduction of apiVersion = 2, the original implementation is formally deprecated, and support for apiVersion 1 will be removed in a future major update to Photoshop. As such a number of new features are only available for apiVersion 2 plugins. apiVersion 2 only features include: new DOM v2, support for suspend and resume of multiple history states at the same time.

Expand All @@ -57,11 +67,9 @@ The apiVersion field is optional. Its default value depends on the version of Ph
* A plugin whose minimum version is less than 23.0.0, will be assigned an apiVersion of 1 unless the plugin specifies otherwise in its manifest.
* A plugin whose minimum version is 23.0.0, or greater, will be assigned an apiVersion of 2 unless the plugin specifies otherwise in its manifest.

### Why downgrade?
### Why use version 1 (2021)?

Most plugins will be able to use apiVersion 2.

Specialized plugins that rely on the Photoshop menu state, or on other non-modal user interactions while the plugin is running may need to stay with apiVersion 1 until such use cases are fully supported by Photoshop.
At this point, we expect almost all plugins to be able to use apiVersion 2. There were some early limitations that we believe are now addressed by executeAsModal's interactive mode.

## loadEvent

Expand All @@ -74,3 +82,10 @@ Certain use cases require that a plugin is loaded when Photoshop is launched. Su
The loadEvent field allows a plugin to specify when it is loaded. The possible values are:
* `use`: The plugin is loaded when needed (when its UI become visible, or when a plugin command is executed). This is the default value.
* `startup`: The plugin is loaded automatically shortly after Photoshop is launched.


## enableMenuRecording

[Menu items](../index.md#entry-points) (`type: "command"`) defined in the manifest's `entrypoints` will appear under that plugin's submenu under the top-level Plugins menu. Those menu items can be recorded as Action steps by setting `enableMenuRecording` to `true`. Once set, those menu items will appear in a recorded Action as "Plugin Menu Command" along with the label of the menu item.

![Actions panel entry](./menu_recording.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/pages/ps_reference/changelog/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ description: Contains a running log of changes to the UXP API environment in Ado
# Photoshop API Changelog

## Photoshop Beta (July/August 2023)

### Action Recording
Adds the capability for UXP plugins to record and playback Action steps.
See Advanced > [Action Recording](../media/action-recording) for usage details.

### batchPlay option: suppressProgressBar
This [value](../media/batchplay#command-execution-options) can be used to suppress a Photoshop progress bar while the command is being executed.
### Selection

In [Document.selection](../classes/document#selection) you can now find the new [Selection](../classes/selection) class for handling pixel selection.
Expand Down
6 changes: 3 additions & 3 deletions src/pages/ps_reference/classes/photoshop.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ no transparency.

```javascript
// "Default Photoshop Size" 7x5 inches at 300ppi
let newDoc1 = await app.documents.add();
let newDoc2 = await app.documents.add({
let newDoc1 = await app.createDocument();
let newDoc2 = await app.createDocument({
width: 800,
height: 600,
resolution: 300,
mode: "RGBColorMode",
fill: "transparent"
});
let newDoc3 = await app.documents.add({preset: "My Default Size 1"});
let newDoc3 = await app.createDocument({preset: "My Default Size 1"});
```

#### Parameters
Expand Down
9 changes: 4 additions & 5 deletions src/pages/ps_reference/classes/selection.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ UI Location: Select > Modify > Contract
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `by` | *number* | - | The amount to contract the selection (integer in the range 1..500). |
| `applyEffectAtCanvasBounds` | *boolean* | false | If true and the selection is outside of canvas, the effect is not limited by canvas bounds. |
| `applyEffectAtCanvasBounds` | *boolean* | false | By default this is false, meaning that any part of the selection that touches the bounds of the canvas will not be affected by the contraction. |

___

Expand Down Expand Up @@ -120,7 +120,7 @@ UI Location: Select > Modify > Expand
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `by` | *number* | - | The amount to expand the selection (integer in the range 1..500). |
| `applyEffectAtCanvasBounds` | *boolean* | false | If true, the selection can expand beyond the canvas bounds. |
| `applyEffectAtCanvasBounds` | *boolean* | false | By default this is false, meaning that any part of the selection that touches the bounds of the canvas will not be affected by the expansion. |

___

Expand All @@ -144,7 +144,7 @@ UI Location: Select > Modify > Feather
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `by` | *number* | - | The amount to feather the selection with (integer in the range 0.1..1000). |
| `applyEffectAtCanvasBounds` | *boolean* | false | If true, the feathered selection can expand beyond the canvas bounds. |
| `applyEffectAtCanvasBounds` | *boolean* | false | By default this is false, meaning that any part of the selection that touches the bounds of the canvas will not be affected by the feathering. |

___

Expand Down Expand Up @@ -410,7 +410,6 @@ ___
Make an elliptical selection.

```javascript
const doc = app.activeDocument;
await doc.selection.selectEllipse({top: 0, left: 0, bottom: 100, right: 100});
```

Expand Down Expand Up @@ -527,7 +526,7 @@ UI Location: Select > Modify > Smooth...
| Name | Type | Default value | Description |
| :------ | :------ | :------ | :------ |
| `radius` | *number* | - | The sample radius in pixels (integer in the range 1..500) |
| `applyEffectAtCanvasBounds` | *boolean* | false | If false, the selection will be trimmed to fit inside canvas bounds |
| `applyEffectAtCanvasBounds` | *boolean* | false | By default this is false, meaning that any part of the selection that touches the bounds of the canvas will not be affected by the smoothing. |

___

Expand Down
87 changes: 87 additions & 0 deletions src/pages/ps_reference/media/action-recording.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
id: "action-recording"
title: "Action Recording"
sidebar_label: "ActionRecording"
---

# Recording an Action Step Provided by a UXP Plugin

While recording an Action via the Photoshop Actions panel, a UXP plugin can add a step by using [`require('photoshop').action.recordAction`](./photoshopaction/#recordaction). The recorded step will include a function name and an object holding provided data. In this way, the Action step will execute the function with the data when played.

## recordAction

The following method from the Action module will add a step to the Action that is being recorded:

```javascript
const PhotoshopAction = require('photoshop').action;
PhotoshopAction.recordAction({"name": "My Command", "methodName": "actionHandler"}, {"prop": value});
```

Later when the recorded Action is invoked, the named top-level JavaScript function is invoked:

```javascript
async function actionHandler(executionContext, info) {
let propValue = info["prop"];
}
```
The `executionContext` argument conforms to the argument used by [executeAsCommand](./executeasmodal). There is no usage of the context related to Action recording at this time.


Example:
```javascript
// Start recording an Action, then execute the code below.
async function alertHandler(context, dataObj) { require('photoshop').core.showAlert(dataObj.message); };
require('photoshop').action.recordAction({"name": "Hello Alert", "methodName": "alertHandler"}, {message: "Hello!"});
// Stop recording, and play the Action.
```
![recordAction](./assets/recordAction.png)

You can run the above in the UDT console of a loaded plugin or the Playground to try it.
Note: `recordAction` is not compatible with Script execution (.psjs files), since there is not a plugin holding the function declaration in its environment.

### Signature

```typescript
async function recordAction(
{
name: string,
methodName: string
},
info: Object ): Promise<void>
```

### Arguments

|argument|description|
|---|---|
|name|User visible string. Used by the Actions panel to display the name of the Action step.|
|methodName|Name of top level JavaScript function. This method is invoked when the recorded Action is executed from the Actions panel.|
|info|Object with data to be used by the above function. This is recorded with the Action and then provided to the target JavaScript function when the recorded Action is played. The properties in `info` will be shown in the Actions panel if the property name is a recognized property name. The list of recognized property names will change between Photoshop versions. The best way to see names of known properties is to find an Action with the desired name and then use `Copy As JavaScript` to inspect the corresponding identifier. If you have internal properties that should not be shown, then you can add a unique prefix. For example your reverse url: `com.foo.myProperty`.|


### Additional Notes

The function (`methodName`) must must be declared at the `global` level to be called later at playback.
- async actionHandler(c, d) => {...
- async function actionHandler(c, d) {...
- actionHandler = (c, d) => {...
- var actionHandler = (c, d) => {...

Avoid due to scope:
- let actionHandler = (c, d) => {...

Also, keep in mind that only the **name** of the function is recorded in the Action step. The function body remains in the environment of the plugin. As such, the function declaration can change, for example, by way of an update to the plugin itself. The Action step will still call it with the data object that was recorded in the step.

An info argument is required. If your function does not need any data to be recorded, you still must pass an empty object literal, `{}`.
### Errors
If your recording fails, try adding a `.catch` to the `recordAction` call.
If you see the error dialog "Script action failed.",
- confirm that your `methodName` is accessible in the global scope of your plugin.
- try calling your function with the `info` object provided in the `recordAction` call.
|Console error|Situation|
|-------------|---------|
|`Unsupported napi type`|Passed a value other than `string` assigned to "methodName"|
|`Failed Error: Argument 2 is missing`|Missing `info` argument|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit bee3ecf

Please sign in to comment.