Migrating from Manifest 4 to 5 and UXP Photoshop.app.open not opening files

I need someone help clarify a things clear here. On manifest 4 I was using
photoshop.app.open(file) and it was working correctly. I was getting the file from the server and opening it correctly. But on migrating to manifest 5 the file doesn’t open. yes the file gets downloaded successfully from my server to the disk but on trying to fetch it nothing happens. not even an error is thrown. What could be the reason since I’ve gone through the documentation and this change of things ain’t mentioned anywhere. Below is my code.

import photoshop from 'photoshop'
import uxp from 'uxp'

const fs = uxp.storage.localFileSystem

export const openMyFileAsDocument = (file: uxp.storage.File) => {
  console.log('opening file as document ', file)
  try {
    return photoshop.app.open(file as unknown as File)
  } catch (error) {
    console.log('error openng file as document', error)
  }
}

Besides, open the file as a layer also doesn’t seem to work on 5 as on 4. Am not sure where m getting it wrong since no exception is thrown

const openTheFileAsAlayer = (
  file: uxp.storage.File,
  replaceSelected?: boolean,
) => {
  console.log('opening file as layer ', file)
  if (photoshop.app.activeDocument) {
    return photoshop.action.batchPlay(
      [
       
        {
          _obj: replaceSelected ? 'placedLayerReplaceContents' : 'placeEvent',
          null: {
          
            _path: fs.createSessionToken(file),
            _kind: 'local',
          },
        },
      ],
      {
        synchronousExecution: false,
        modalBehavior: 'fail',
      },
    )
  } else {
    return // do something else
  }
}

file as unknown as File

what does this mean?

and how are you getting the entry to that file? I don’t see that in the code


for the 2nd issue you’ll need to use executeAsModal and remove modalBehavior: 'fail'

the As file was typescript version for it. the above is the same code but without typescript

  1. have you added Local Filesystem requiredPermissions to your manifest?
    "requiredPermissions": {
        "localFileSystem": "fullAccess"
    }
  1. make sure the file is a valid entry.
  2. wrap all related code in a try/catch and share whatever is logged in the debugger.

if all fails:
I assume you’re using webpack since vanilla doesn’t support import
could you run a minimal vanilla test to make sure everything is good and go from there?

async function openFile() {
    const fs = require("uxp").storage.localFileSystem;
    const app = require("photoshop").app;
    const f = await fs.getFileForOpening();
    await app.open(f);
}

I changed the code to using executeModal and the problem migrated to the following. Error: Parameter must be a valid entry representing a file or a folder
below is the same code but with execute as modal and try catch

export const openFileAsDocument = async (file) => {
  console.log('opening file as document ', file)
  async function openDocument(file) {
    return await photoshop.app.open(file)
  }
  try {
    return await require('photoshop').core.executeAsModal(openDocument, {
      command: 'my script',
    })
  } catch (error) {
    console.log('error openng file as document', error)
  }
}

below is my uxp log

I’ve also granted fullAccess to localFileSystem

I think your async function openDocument(file) doesn’t actually recieve that argument file and inside the function it becomes null. Try changing to

executeAsModal(() => openDocument(file), ...)

And avoid using identical names in the same scope, like

func1(name) {
  func2(name) {
    // ...
  }

  func2()
}

Change to

func1(name1) {
  func2(name2) {
    // ...
  }

  func2(name1)
}

This way you would notice issues more quickly

the entry object logged seems to be valid.
however the argument received by openDocument is not what you think.

functions called by executeAsModal recieve executionContext as first argument

please try the following:

const app = require("photoshop").app;
const core = require('photoshop').core;

export const openFileAsDocument = async (file) => {
    core.executeAsModal(async (executionContext) => {
        await app.open(file)
       // the rest of your code here
}, { "commandName": "Opening File" })
}

thanks alot. it has worked. posting it for someone else who might get stack. This is in typescript format.

export const openFileAsDocument = async (file) => {
  console.log('opening file as document ', file)
  async function openDocument() {    
    return await photoshop.app.open(file)
  }
  try {
    return await require('photoshop').core.executeAsModal(openDocument, {
      command: 'opening file',
    })
  } catch (error) {
    console.log('error opening file as document', error)
  }
}

the only issue is, I see this logged on the console. Error: document Return value could not be coerced to action descriptor

Where exactly do we make use of the execution context. I’ve seen it in the documentation and I’m unable to figure it out properly, with both the descriptor.
According to my understanding of the documentation, the target function refers to the function passed as an argument to execute as modal. so in my case above the OpenDocument function is the target function.

Then the documentation says that the target function again takes in the executionContext and the descriptor?

Now I get more confused .
here is the pic

executionContext can be used to suspendHistory as follows:

   let hostControl = executionContext.hostControl;
   let suspensionID = await hostControl.suspendHistory({
      "documentID": app.activeDocument.id,
      "name": "My Plugin Command"
   });

  // your code here

 await hostControl.resumeHistory(suspensionID);

this will create an entry in history called “My Plugin Command” that can be used for undo / redo.

we can also use executionContext to reportProgress
it will update the progress bar acoording to set value (0 to 1)

executionContext.reportProgress({"value": 0.5});

the method open returns a document object

what are you trying to do with it?

I only want to open that document and set xmp metadata. That’s it