Confused about suspend history usage

Hello guys, this may be a silly question but I am not sure how to use the suspend History with my functions. for example many of my code structured this way:

document.getElementById("btn").addEventListener("click", async function () {
  await ExecuteAsModal(() => doStaff());
});
async function doStaff() {
  // function 1
  // function 2
  // batchPlay
  //batchPlay
}

In the documentation it says we can use this code: I don’t know where should I add it. any help clarifying this and any example would be much appreciated.

async function historyStateSample(executionContext) {
    let hostControl = executionContext.hostControl;

    // Get an ID for a target document
    let documentID = await getTargetDocument();

    // Suspend history state on the target document
    // This will coalesce all changes into a single history state called
    // 'Custom Command'
    let suspensionID = await hostControl.suspendHistory({
        "documentID": documentID,
        "name": "Custom Command"
    });

    // modify the document
    // . . .

    // resume the history state
    await hostControl.resumeHistory(suspensionID);
}


btw I am in api 2, manifest 5

1 Like

Sorry for my OCD, but I think you meant doStuff() :sweat_smile:

That said, docs are full of bugs and the page is very inconsistent - in some places it’s executionContext and in others it’s executionControl. So basically your target function receives that as a first argument

Your code could be something like:

document.getElementById("btn").addEventListener("click", async function () {
  await ExecuteAsModal(doStuff);
});

async function doStuff(executionContext) {
  let hostControl = executionContext.hostControl;
  let documentID = await getTargetDocument();

  let suspensionID = await hostControl.suspendHistory({
    "documentID": documentID,
    "name": "Custom Command"
  });

  // function 1
  // function 2
  // batchPlay
  //batchPlay

  await hostControl.resumeHistory(suspensionID);
}
1 Like

The documentation make me more confused to be honest. Yeah its should be doStuff() :smile:
I made this simple batchPlay to create new layer and convert to smart object to test it, but when I press the button nothing happens.


document.getElementById("btn").addEventListener("click", async function () {
  await ExecuteAsModal(doStuff());
});

async function doStuff(executionContext) {
  let hostControl = executionContext.hostControl;
  let documentID = await getTargetDocument();

  let suspensionID = await hostControl.suspendHistory({
    documentID: documentID,
    name: "New Layer",
  });

  // new SO
  await batchPlay(
    [
      {
        _obj: "make",
        _target: [{ _ref: "layer" }],
        using: { _obj: "layer", name: "New Layer" },
      },
      { _obj: "mergeVisible", duplicate: true },
      { _obj: "newPlacedLayer" },
    ],
    {}
  );

  await hostControl.resumeHistory(suspensionID);
}

Too many parentheses. Should be:

await ExecuteAsModal(doStuff)

Note just doStuff instead of doStuff()

Also - do you have a function getTargetDocument()? This is from the example in the docs. You should get your document yourself. Maybe app.activeDocument?.id would do if you want it for active document

1 Like

Thank you @Karmalakas it works now :smile:
here is the whole code for others who need it:


document.getElementById("btn").addEventListener("click", async function () {
  await ExecuteAsModal(doStuff);
});

async function doStuff(executionContext) {
  let hostControl = executionContext.hostControl;
  // let documentID = await getTargetDocument();

  let suspensionID = await hostControl.suspendHistory({
    documentID: app.activeDocument?.id,
    name: "New Layer",
  });

  // new SO
  await batchPlay(
    [
      {
        _obj: "make",
        _target: [{ _ref: "layer" }],
        using: { _obj: "layer", name: "New Layer" },
      },
      { _obj: "mergeVisible", duplicate: true },
      { _obj: "newPlacedLayer" },
    ],
    {}
  );

  await hostControl.resumeHistory(suspensionID);
}

1 Like

I’m not sure if the code works on API2. Following is verified working code on API 2 with UXP Developer tool console for someone needs.

async function doStuff(executionContext) {
    let result = null; // batchPlay()

    await require('photoshop').core.executeAsModal(async function() { // await executeAsModal (API 2)
        result = await require('photoshop').action.batchPlay([ // await batchPlay
            {
                _obj: "make",
                _target: [
                    {
                        _ref: "layer"
                    }
                ],
                using: {
                    _obj: "layer",
                    name: "New Layer"
                }
            },
            {
                _obj: "mergeVisible",
                duplicate: true
            },
            {
                _obj: "newPlacedLayer"
            }
        ], {synchronousExecution: false}) // false = async
    });
    return result;
}

async function doStuffWithSuspendHistory(executionContext) {
    let result = null; // batchPlay()
    let hostControl = executionContext.hostControl;

    let suspensionID = await hostControl.suspendHistory({
        documentID: require('photoshop').app.activeDocument.id,
        name: "New Layer (test)", // History name
    });
    result = await doStuff();
    await hostControl.resumeHistory(suspensionID);
}

// await require('photoshop').core.executeAsModal(doStuff); // without suspend history
await require('photoshop').core.executeAsModal(doStuffWithSuspendHistory);
1 Like

Does anyone know if this works with API2. I am trying to add a button to the above code but haven’t had luck running it:

document.getElementById("test").addEventListener("click", doStuff);
async function doStuff(executionContext) {
    let result = null; // batchPlay()

    await require('photoshop').core.executeAsModal(async function() { // await executeAsModal (API 2)
        result = await require('photoshop').action.batchPlay([ // await batchPlay
            {
                _obj: "make",
                _target: [
                    {
                        _ref: "layer"
                    }
                ],
                using: {
                    _obj: "layer",
                    name: "New Layer"
                }
            },
            {
                _obj: "mergeVisible",
                duplicate: true
            },
            {
                _obj: "newPlacedLayer"
            }
        ], {synchronousExecution: false}) // false = async
    });
    return result;
}

async function doStuffWithSuspendHistory(executionContext) {
    let result = null; // batchPlay()
    let hostControl = executionContext.hostControl;

    let suspensionID = await hostControl.suspendHistory({
        documentID: require('photoshop').app.activeDocument.id,
        name: "New Layer (test)", // History name
    });
    result = await doStuff();
    await hostControl.resumeHistory(suspensionID);
}

// await require('photoshop').core.executeAsModal(doStuff); // without suspend history
await require('photoshop').core.executeAsModal(doStuffWithSuspendHistory);

with a simple html:

<!DOCTYPE html>
<html>
<head>
    <script src="main.js"></script>
    <link rel="stylesheet" href="style.css">
</head>
<body>
  <sp-button id="test">test me</sp-button>
  <footer>
  </footer>
</body>
</html>