TS support for UXP and Photoshop

It wouldn’t be that much sophisticated. You will be able to mix together impossible combinations. But those properties and values has to have type definition in first place.

Anyway I do not expect Inspector or Listener to record non-sense therefore this won’t be issue here. But if someone wants to create make action from scratch based on intellisense only then good luck. That event name is used by everything. Make layer, make channel, make path, …

Well, sometimes even nonsense can lead to very interesting results…
Did you know that you can turn a layer icon color to black (which is no color option you can choose on the UI)? This happens when you set the color to a wrong value :joy:
What a shame it resets to “none” after reopening the PSD :sweat_smile:

1 Like

I did. Sometimes devs are working on some feature but they don’t expose it since it is not yet ready. But then priorities can change and they leave it there and those parts might or might not work when you try to script them. E.g. text columns

Hi all, I tried to convert the Adobe documentation into Typescript types. You can find it here: GitHub - hansottowirtz/adobe-uxp-types-crawler: Crawls the Adobe UXP documentation and generates Typescript declaration files.

npm i -D adobe-uxp-types-photoshop

@simonhenke Would you mind if I copy your descriptor types and bundle them with this package?

Would it work if I make them available through npm and you include them as a dependency?
Also got the complete types for the document descriptor, just forgot to push to the repo the last weeks…

That might work, but then all the enums should be part of the photoshop module, because there might otherwise be collisions between modules. I think a better approach might be to include them straight in the repo, because dependency management between types is quite annoying, and I think it would be good to have a single source for all types. I was also thinking to flatten all types to a single photoshop.d.ts file for publishing. What do you think? Can I make you a contributor on the repo in order to add the types over there?

@kerrishotts can you please publish types that are in pre-release?

@Jarda I have access to the Prerelease program but can’t seem to find anything about types there. Can you let me know how to find them?

Technically it is under NDA so even this conversation shouldn’t exist here. Therefore I would prefer Adobe to make that repo public.

You could also copy them there I guess, if that’s easier. (Maybe add a backlink?).
I haven’t fully checked the crawler code yet, but I’m not sure if an automated process gives perfect results or makes things too chaotic.
I plan to update the types also (working on the full app descriptor etc) and would prefer to update them in the more organized repo instead of two repos parallely.

Also, there might be some overlaps for example:

 enum WarpStyle {
    /**
     * The type is warped in the shape of an arc.
     */
    ARC = 2,

    /**
     * Text is warped in the form of an arch.
     */
    ARCH = 5,
...
}

which you inserted from that other repo has numeric values for the enum. Are these from the old ExtendScript API? (which is obsolete).

The types I wrote are for the internal structure of Photoshop and it’s descriptors, in that specific case it would be:

export enum WarpStyle {
  warpNone = 'warpNone',
  warpArc = 'warpArc',
  warpArcLower = 'warpArcLower',
...
}

Indeed, I thought these interfaces would be the same, but they’re not. I kept only SaveOptions from Types-for-Adobe which still looks correct. I have added your types to the package, added a backlink and invited you as a collaborator.

There are indeed some serious limitations to crawling, but there are overrides possible in res/entrypoints.jsonc and res/templates/photoshop/0_manual.d.ts, which provides a lot of interfaces that are missing in the documentation. Nonetheless, it’s a good starting point for creating manual definitions.

I think it would be great to create a definition for batchPlay using your descriptors next, right now it’s still a lot of anys, but I’m not sure how to start.

Also feel free to move this discussion elsewhere.

Ok nice, looks good so far :slight_smile: I’ll let you know once I’ll push the Document & App Descriptor to my types repo. I also added or changed some things in the existing files which aren’t on the remote yet, what would be the best practice to keep your repo in sync? For the start you could update it manually I guess.

The API related types will be more complete once the Adobe Team publishes the ones that already exists.

What exactly do you mean? I also already thought about ways to simplify the batchPlay calls, or giving it some structrue at least.

I wrote some universal getters already, that are fully type-safe (for layer, document & app):

export function getLayerProperty<T extends keyof LayerDescriptor>(_property: T, id?: number): LayerDescriptor[T]{
  return photoshop.action.batchPlay([
    {
      _obj: 'get',
      _target: [{ _property }, id ? layerRefID(id) : layerRef],
    },
  ], { synchronousExecution: true })[0][_property]
}

export function getLayerProperties(props: Array<keyof LayerDescriptor>, id?: number): Partial<LayerDescriptor> {
  const res = photoshop.action.batchPlay(
    [
      {
        _obj: 'multiGet',
        _target: id ? layerRefID(id) : layerRef,
        extendedReference: [props],
      },
    ],
    { synchronousExecution: true }
  )[0]
  return res
}

That’s quite fun as it gives auto complete, type check and the return value will also have the correct type.

But since these are functions, I think they don’t quite fit into a types repo. I thought about creating some kind of utility package for these kinds of helpers.

For setters it will be a bit more complicated as every event or operation requires it’s own unique subset of keys of a descriptor for it to work correctly…

Great! I think it would be easiest to create a Pull Request on the repo with the new types, but I’ll do it manually for now.

I meant creating autocomplete for the batchPlay methods, right now it’s just this:

  interface App {
    batchPlay(commands: ActionDescriptor[], options?: BatchPlayCommandOptions): Promise<Descriptor[]>;
  }

  interface ActionDescriptor {
    _obj: string;
    _target: {
      _ref: string;
      _enum: string;
      _value: string;
    };
    [key: string]: any;
  }

  type Descriptor = ActionDescriptor;

which is both incorrect and incomplete.

Any idea how we could use your existing work to type this thing? Should Descriptor be a union of the descriptors you already made? (like type Descriptor = LayerDescriptor | Adjustment). I’m a bit confused.

I also had a look at Alchemist’s source code which uses Adobe’s private types (just to make sure we aren’t doing double work). It seems like their batchPlay isn’t fully typed either, see:
alchemist/GetInfo.ts at master · jardicc/alchemist · GitHub. Theres also some inspiring types in that file, I’ll have a look tomorrow if I can add them to the package as well. (If you haven’t already added them to your updates)

I agree creating a helper library would be nice, it gets pretty confusing! :slight_smile:

I wouldn’t assemble the Descriptor / ActionDescriptor type from other types - that would be incorrect and also hardly maintainable, as Descriptors will be added with every new feature.
Their types are actually quite uncomplicated.
A Descriptor is literally an object (so it can have any key+value pair) and an ActionDescriptor must have an _obj key and any additional key/value pair.

You could of course write ActionDescriptor types for specific events, something like "setLayerEffectsDescriptor":

export interface PropertyReference<Property extends string = ''> {
  _ref: 'property'
  _property: Property
}

export interface LayerReference {
  _ref: 'layer'
}

export interface SetLayerEffectsDescriptor {
  _obj: 'set',
  _target: [
    PropertyReference<'layerEffects'>,
    LayerReference,
  ],
  to: Partial<LayerEffectsDescriptor>,
}

which can then be used to type-savely create the following function:

export function __setLayerEffects(layerEffects: Partial<LayerEffectsDescriptor>, id?: number): SetLayerEffectsDescriptor {
  return {
    _obj: 'set',
    _target: [
      {
        _ref: 'property',
        _property: 'layerEffects',
      },
      id ? layerRefID(id) : layerRef,
    ],
    to: layerEffects,
  }
}

I don’t really see that much use in it though… I was able to write the function also without the specific typing - the return type could just be ActionDescriptor or any after all. You’re just gonna throw it into a batchPlay call, so the type of it doesn’t really matter, does it? For getters in comparison, you keep working on their return value after calling them, so it’s helpful to have the types.

In theory you could abstract the above types even more until you have something like

export interface setPropertyDescriptor<PropertyType, PropertyName extends string = ''> {
  _obj: 'set',
  _target: [
    PropertyReference<PropertyName>,
    Reference,
  ],
  to: Partial<PropertyType>,
}

However, what would you do with it then. Well, you could easily write a function to set any property of any class object in Photoshop (Layer, App, Document, …), but it would be quite an abstract function. I’d prefer more specific functions, such a the setLayerEffects() from above.


Nope, didn’t add anything from there.
I just finished (99% of) the Application Descriptor which was quite exhausting :sweat_smile:
I’ll push this, the Document Descriptor and some Reference Types to my repo tomorrow.

:wave: Just to add to the types pile, I ended up building this https://github.com/thejustinwalsh/uxp-types so I can just add the typeRoots to my projects and be off to the races with uxp-types.

Would be great If all the types that are out there could be structured or kitbashed into a single npm package that makes it super easy to drop in like the example in my repo.

Up next, we need JSX Intrinsic types for React, working on a package, but it anyone else knows if this is defined somewhere else please drop me a note.

Need all of these for spectrum components.

declare namespace JSX {
  interface IntrinsicElements {
      'sp-heading': {};
      'sp-divider': { size: "large" };
      'sp-body': { children?: React.ReactNode, class?: string };
      'sp-icon': { children?: React.ReactNode, name?: string, size?: string };
      'sp-detail': { children?: React.ReactNode };
      'sp-button-group': { children?: React.ReactNode };
      'sp-button': { children?: React.ReactNode, tabindex?: number, variant?: string, quiet?: string, autofocus?: string, onClick?: (e:Event) => void};
      'sp-slider': { children?: React.ReactNode, ref: any, "data-part"?: string, value: number, min: number, max: number };
      'sp-label': { children?: React.ReactNode, slot: string };
      'sp-textfield': { children?: React.ReactNode, ref: any, "data-part"?: string, type: string, value: number, min: number, max: number };
  }
}

1 Like

Thanks. I will test it in my next project.

I agree. There should be one source of types with everything in it. It is just types there is not much room to do it differently.

It would be best if Adobe would provide official types. E.g. those types in private repo could be finally public after a year and significantly updated.

Going to break this out into a new thread, but if interested in testing before the announcement, I created a set of React Components for use in UXP using spectrum.

These are hot off the press, and I am working through building a starter template app now.

2 Likes

Oh, this is AWESOME! :tada:

2 Likes

It looks like something I will use.

WIP Starter project exercising the above as well. Looking for early feedback.