@ps-generator-bridge/generator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/contract-C4vydf6-.d.ts +1270 -0
- package/dist/contract.d.ts +3 -0
- package/dist/contract.js +19 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2482 -0
- package/dist/index.js.map +1 -0
- package/jsx/Action/autoCutout.jsx +12 -0
- package/jsx/Action/redo.jsx +5 -0
- package/jsx/Action/removeBackground.jsx +10 -0
- package/jsx/Common/alert.jsx +4 -0
- package/jsx/Common/debug.jsx +43 -0
- package/jsx/Common/event-dispatch.jsx +4 -0
- package/jsx/Document/exportDocument.jsx +128 -0
- package/jsx/Document/getDocumentInfo.jsx +222 -0
- package/jsx/Document/openPsd.jsx +6 -0
- package/jsx/Document/openPsdFile.jsx +7 -0
- package/jsx/Document/saveDocument.jsx +37 -0
- package/jsx/Layer/addImageLayer.jsx +182 -0
- package/jsx/Layer/createSelectionStroke.jsx +44 -0
- package/jsx/Layer/getActiveLayerID.jsx +1 -0
- package/jsx/Layer/getLayerBounds.jsx +114 -0
- package/jsx/Layer/getLayerInfo.jsx +323 -0
- package/jsx/Layer/getLayerPixmap.jsx +337 -0
- package/jsx/Layer/getSelection.jsx +6 -0
- package/jsx/Layer/layer.jsx +284 -0
- package/jsx/Layer/saveEngineDataToLayer.jsx +26 -0
- package/jsx/Layer/setLayerWorkpathMask.jsx +126 -0
- package/jsx/Layer/transformLayer.jsx +121 -0
- package/jsx/Selection/getSelectionPath.jsx +389 -0
- package/jsx/Selection/pathtosvg.jsx +257 -0
- package/jsx/Selection/registerEvent.jsx +10 -0
- package/jsx/Selection/selectiontopath.jsx +9 -0
- package/jsx/polyfills/Array.js +159 -0
- package/jsx/polyfills/Function.js +29 -0
- package/jsx/polyfills/JSON.js +182 -0
- package/jsx/polyfills/Number.js +24 -0
- package/jsx/polyfills/Object.js +80 -0
- package/jsx/polyfills/String.js +87 -0
- package/jsx/types/extendscript/README.md +34 -0
- package/jsx/types/extendscript/actions.d.ts +148 -0
- package/jsx/types/extendscript/application.d.ts +74 -0
- package/jsx/types/extendscript/channel.d.ts +70 -0
- package/jsx/types/extendscript/color.d.ts +92 -0
- package/jsx/types/extendscript/document.d.ts +110 -0
- package/jsx/types/extendscript/enums.d.ts +796 -0
- package/jsx/types/extendscript/index.d.ts +504 -0
- package/jsx/types/extendscript/layer.d.ts +530 -0
- package/jsx/types/extendscript/misc.d.ts +274 -0
- package/jsx/types/extendscript/open-options.d.ts +86 -0
- package/jsx/types/extendscript/path.d.ts +196 -0
- package/jsx/types/extendscript/save-options.d.ts +144 -0
- package/jsx/types/extendscript/selection.d.ts +191 -0
- package/jsx/types/extendscript/text.d.ts +169 -0
- package/main.js +16 -0
- package/package.json +75 -0
|
@@ -0,0 +1,1270 @@
|
|
|
1
|
+
import { Stream } from 'node:stream';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
|
|
4
|
+
type PsBounds$1 = {
|
|
5
|
+
left: number;
|
|
6
|
+
right: number;
|
|
7
|
+
top: number;
|
|
8
|
+
bottom: number;
|
|
9
|
+
};
|
|
10
|
+
type PsRect = {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
type PsPixmap = {
|
|
17
|
+
format: number;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
rowBytes: number;
|
|
21
|
+
colorMode: number;
|
|
22
|
+
channelCount: number;
|
|
23
|
+
bitsPerChannel: number;
|
|
24
|
+
pixels: Buffer;
|
|
25
|
+
bytesPerPixel: number;
|
|
26
|
+
padding: number;
|
|
27
|
+
bounds: PsBounds$1;
|
|
28
|
+
resolution: number;
|
|
29
|
+
getPixel: (n: number) => any;
|
|
30
|
+
readChannel: (n?: number) => any;
|
|
31
|
+
};
|
|
32
|
+
interface PsDocumentInfo {
|
|
33
|
+
version: string;
|
|
34
|
+
timeStamp: number;
|
|
35
|
+
count: number;
|
|
36
|
+
id: number;
|
|
37
|
+
file: string;
|
|
38
|
+
bounds: PsBounds$1;
|
|
39
|
+
selection: number[];
|
|
40
|
+
resolution: number;
|
|
41
|
+
globalLight: {
|
|
42
|
+
angle: number;
|
|
43
|
+
altitude: number;
|
|
44
|
+
};
|
|
45
|
+
generatorSettings?: any;
|
|
46
|
+
profile: string;
|
|
47
|
+
mode: string;
|
|
48
|
+
depth: number;
|
|
49
|
+
layers: any[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The plugin's contract with generator-core, typed faithfully from
|
|
54
|
+
* `generator-core/lib/generator.js` (every public method, the low-level
|
|
55
|
+
* `_sendJSX*` transport, and the underscore-prefixed internals). Public
|
|
56
|
+
* thenable methods are typed as `Promise<T>` (consumers `await` them); the
|
|
57
|
+
* `_sendJSX*` methods return a `Deferred<T>` — generator-core's Q deferred,
|
|
58
|
+
* which streams progress messages for pixmap/shape protocols.
|
|
59
|
+
*
|
|
60
|
+
* This is an interface (not a class): the plugin never `instanceof`-checks the
|
|
61
|
+
* generator and only depends on the surface. A test fake cannot satisfy the
|
|
62
|
+
* full required surface, so tests build one via the `fakeGenerator()` factory
|
|
63
|
+
* (a single `as unknown as FakeGenerator & PsGenerator` cast) — see
|
|
64
|
+
* `test/fakeGenerator.ts`.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* generator-core's Q `Deferred` shape — the return of `_sendJSXString` /
|
|
70
|
+
* `_sendJSXFile`. The promise streams `progress` / `fail` notifications (used
|
|
71
|
+
* by the pixmap/shape protocols) and settles via `resolve` / `reject`. Only the
|
|
72
|
+
* members the plugin reaches are modelled.
|
|
73
|
+
*/
|
|
74
|
+
interface Deferred<T> {
|
|
75
|
+
promise: {
|
|
76
|
+
then<TResult>(
|
|
77
|
+
onFulfilled?: (value: T) => TResult | PromiseLike<TResult>,
|
|
78
|
+
onRejected?: (reason: unknown) => TResult | PromiseLike<TResult>
|
|
79
|
+
): Promise<TResult>;
|
|
80
|
+
progress(fn: (message: { type: string; value: unknown }) => void): void;
|
|
81
|
+
fail(fn: (reason: unknown) => void): void;
|
|
82
|
+
finally(fn: () => void): void;
|
|
83
|
+
};
|
|
84
|
+
resolve(value?: T): void;
|
|
85
|
+
reject(reason?: unknown): void;
|
|
86
|
+
notify(message: unknown): void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
declare namespace PsGenerator {
|
|
90
|
+
/** Settings for `getPixmap` / `getDocumentPixmap` (generator.js:1201-1208). */
|
|
91
|
+
export interface GetPixmapSettings {
|
|
92
|
+
boundsOnly?: boolean;
|
|
93
|
+
inputRect?: PsBounds$1;
|
|
94
|
+
outputRect?: PsBounds$1;
|
|
95
|
+
scaleX?: number;
|
|
96
|
+
scaleY?: number;
|
|
97
|
+
clipBounds?: PsBounds$1;
|
|
98
|
+
useJPGEncoding?: string;
|
|
99
|
+
useSmartScaling?: boolean;
|
|
100
|
+
convertToWorkingRGBProfile?: boolean;
|
|
101
|
+
useICCProfile?: string;
|
|
102
|
+
getICCProfileData?: boolean;
|
|
103
|
+
allowDither?: boolean;
|
|
104
|
+
useColorSettingsDither?: boolean;
|
|
105
|
+
interpolationType?: string;
|
|
106
|
+
forceSmartPSDPixelScaling?: boolean;
|
|
107
|
+
clipToDocumentBounds?: boolean;
|
|
108
|
+
maxDimension?: number;
|
|
109
|
+
compId?: number;
|
|
110
|
+
compIndex?: number;
|
|
111
|
+
includeAncestorMasks?: boolean;
|
|
112
|
+
includeAdjustors?: boolean;
|
|
113
|
+
includeChildren?: boolean;
|
|
114
|
+
includeClipBase?: boolean;
|
|
115
|
+
includeClipped?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Settings for `savePixmap` / `streamPixmap` (generator.js:1959-1977). */
|
|
119
|
+
export interface SavePixmapSettings {
|
|
120
|
+
format: string;
|
|
121
|
+
quality?: number;
|
|
122
|
+
lossless?: boolean;
|
|
123
|
+
ppi?: number;
|
|
124
|
+
padding?: PsBounds$1;
|
|
125
|
+
extract?: { x: number; y: number; width: number; height: number };
|
|
126
|
+
background?: [number, number, number, number];
|
|
127
|
+
_scale?: number;
|
|
128
|
+
usePngquant?: boolean;
|
|
129
|
+
useFlite?: boolean;
|
|
130
|
+
useJPGEncoding?: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Flags overriding `getDocumentInfo` defaults (generator.js:720-748). */
|
|
134
|
+
export interface DocumentInfoFlags {
|
|
135
|
+
compInfo?: boolean;
|
|
136
|
+
imageInfo?: boolean;
|
|
137
|
+
layerInfo?: boolean;
|
|
138
|
+
expandSmartObjects?: boolean;
|
|
139
|
+
getTextStyles?: boolean;
|
|
140
|
+
getFullTextStyles?: boolean;
|
|
141
|
+
selectedLayers?: boolean;
|
|
142
|
+
getCompLayerSettings?: boolean;
|
|
143
|
+
getDefaultLayerFX?: boolean;
|
|
144
|
+
getPathData?: boolean;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A layer spec: a layer id, or an index range with optional hidden indices. */
|
|
148
|
+
export type LayerSpec =
|
|
149
|
+
| number
|
|
150
|
+
| { firstLayerIndex: number; lastLayerIndex: number; hidden?: number[] };
|
|
151
|
+
|
|
152
|
+
/** Result of `getGuides` (generator.js:2047). */
|
|
153
|
+
export interface Guides {
|
|
154
|
+
horizontal: number[];
|
|
155
|
+
vertical: number[];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Result of `getMenuState` / `checkPluginCompatibility`. */
|
|
159
|
+
export interface MenuState {
|
|
160
|
+
enabled: boolean;
|
|
161
|
+
checked: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface PluginCompatibility {
|
|
165
|
+
compatible: boolean;
|
|
166
|
+
message: string | null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Scaling settings for `getPixmapParams` (generator.js:1566-1574). */
|
|
170
|
+
export interface PixmapParamsSettings {
|
|
171
|
+
width?: number;
|
|
172
|
+
height?: number;
|
|
173
|
+
scaleX?: number;
|
|
174
|
+
scaleY?: number;
|
|
175
|
+
scale?: number;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
interface PsGenerator {
|
|
180
|
+
// --- lifecycle -----------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
/** Launch the generator: connect to Photoshop, resolve with self. */
|
|
183
|
+
start(options?: Record<string, any>): Promise<PsGenerator>;
|
|
184
|
+
|
|
185
|
+
/** Disconnect from Photoshop. */
|
|
186
|
+
shutdown(): void;
|
|
187
|
+
|
|
188
|
+
/** Whether the Photoshop connection is currently live. */
|
|
189
|
+
isConnected(): boolean;
|
|
190
|
+
|
|
191
|
+
/** Send a keep-alive; resolves true when Photoshop acknowledges. */
|
|
192
|
+
checkConnection(): Promise<boolean>;
|
|
193
|
+
|
|
194
|
+
// --- jsx evaluation ------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
/** Evaluate a local jsx file with optional params; resolves `message.value`. */
|
|
197
|
+
evaluateJSXFile(
|
|
198
|
+
path: string,
|
|
199
|
+
params?: Record<string, any>,
|
|
200
|
+
sharedEngineSafe?: boolean
|
|
201
|
+
): Promise<any>;
|
|
202
|
+
|
|
203
|
+
/** `evaluateJSXFile` forced into the shared script engine. */
|
|
204
|
+
evaluateJSXFileSharedSafe(path: string, params?: Record<string, any>): Promise<any>;
|
|
205
|
+
|
|
206
|
+
/** Evaluate a jsx string; resolves `message.value`. */
|
|
207
|
+
evaluateJSXString(s: string, sharedEngineSafe?: boolean): Promise<any>;
|
|
208
|
+
|
|
209
|
+
/** `evaluateJSXString` forced into the shared script engine. */
|
|
210
|
+
evaluateJSXStringSharedSafe(s: string): Promise<any>;
|
|
211
|
+
|
|
212
|
+
/** Low-level jsx-string transport (private in generator-core). Returns the
|
|
213
|
+
* raw deferred so callers can subscribe to the progress stream. */
|
|
214
|
+
_sendJSXString(s: string, deferred?: Deferred<any>, sharedEngineSafe?: boolean): Deferred<any>;
|
|
215
|
+
|
|
216
|
+
/** Low-level jsx-file transport (private in generator-core). Returns the raw
|
|
217
|
+
* deferred; the pixmap/shape protocols subscribe to its progress messages. */
|
|
218
|
+
_sendJSXFile(
|
|
219
|
+
path: string,
|
|
220
|
+
params?: Record<string, any>,
|
|
221
|
+
sharedEngineSafe?: boolean
|
|
222
|
+
): Deferred<any>;
|
|
223
|
+
|
|
224
|
+
// --- UI / clipboard ------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
/** Show a Photoshop alert dialog. */
|
|
227
|
+
alert(message: string, stringReplacements?: string): void;
|
|
228
|
+
|
|
229
|
+
/** Copy a string to the system clipboard. */
|
|
230
|
+
copyToClipboard(str: string): void;
|
|
231
|
+
|
|
232
|
+
// --- Photoshop environment ----------------------------------------------
|
|
233
|
+
|
|
234
|
+
/** Resolve to the Photoshop install directory path. */
|
|
235
|
+
getPhotoshopPath(): Promise<string>;
|
|
236
|
+
|
|
237
|
+
/** Resolve to the Photoshop executable directory (inside the .app on Mac). */
|
|
238
|
+
getPhotoshopExecutableLocation(): Promise<string>;
|
|
239
|
+
|
|
240
|
+
/** Resolve to the Photoshop locale string. */
|
|
241
|
+
getPhotoshopLocale(): Promise<string>;
|
|
242
|
+
|
|
243
|
+
/** Resolve to the Photoshop version, e.g. "19.0.0". */
|
|
244
|
+
getPhotoshopVersion(): Promise<string>;
|
|
245
|
+
|
|
246
|
+
// --- menus ---------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
/** Register a menu item; resolves once Photoshop has rebuilt the menu. */
|
|
249
|
+
addMenuItem(name: string, displayName: string, enabled: boolean, checked: boolean): Promise<void>;
|
|
250
|
+
|
|
251
|
+
/** Toggle (and optionally rename) an existing menu item. */
|
|
252
|
+
toggleMenu(name: string, enabled: boolean, checked: boolean, displayName?: string): Promise<void>;
|
|
253
|
+
|
|
254
|
+
/** Read the enabled/checked state of a menu item, or null if absent. */
|
|
255
|
+
getMenuState(name: string): PsGenerator.MenuState | null;
|
|
256
|
+
|
|
257
|
+
// --- documents -----------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
/** Resolve to the ids of all open documents. */
|
|
260
|
+
getOpenDocumentIDs(): Promise<number[]>;
|
|
261
|
+
|
|
262
|
+
/** Resolve to a document's info (layers, comps, image, …); rejects with
|
|
263
|
+
* "No Open Document" when none is open. */
|
|
264
|
+
getDocumentInfo(
|
|
265
|
+
documentId?: number,
|
|
266
|
+
flags?: PsGenerator.DocumentInfoFlags
|
|
267
|
+
): Promise<PsDocumentInfo>;
|
|
268
|
+
|
|
269
|
+
// --- generator settings --------------------------------------------------
|
|
270
|
+
|
|
271
|
+
/** Get a layer's generator settings for a plugin. */
|
|
272
|
+
getLayerSettingsForPlugin(documentId: number, layerId: number, pluginId: string): Promise<any>;
|
|
273
|
+
|
|
274
|
+
/** Set a layer's generator settings for a plugin. */
|
|
275
|
+
setLayerSettingsForPlugin(
|
|
276
|
+
settings: Record<string, any>,
|
|
277
|
+
layerId: number,
|
|
278
|
+
pluginId: string
|
|
279
|
+
): Promise<any>;
|
|
280
|
+
|
|
281
|
+
/** Get document-wide generator settings for a plugin. */
|
|
282
|
+
getDocumentSettingsForPlugin(documentId: number, pluginId: string): Promise<any>;
|
|
283
|
+
|
|
284
|
+
/** Set document-wide generator settings for a plugin. */
|
|
285
|
+
setDocumentSettingsForPlugin(
|
|
286
|
+
settings: Record<string, any>,
|
|
287
|
+
documentId: number,
|
|
288
|
+
pluginId: string
|
|
289
|
+
): Promise<any>;
|
|
290
|
+
|
|
291
|
+
/** Extract and parse generator settings from a document object. */
|
|
292
|
+
extractDocumentSettings(document: Record<string, any>, pluginId?: string): any;
|
|
293
|
+
|
|
294
|
+
// --- Photoshop events ----------------------------------------------------
|
|
295
|
+
|
|
296
|
+
/** Subscribe to one or more Photoshop events over the connection. */
|
|
297
|
+
subscribeToPhotoshopEvents(events: string | string[]): Promise<boolean>;
|
|
298
|
+
|
|
299
|
+
/** Register a listener for a Photoshop event. */
|
|
300
|
+
onPhotoshopEvent(event: string, listener: (event: any) => void): void;
|
|
301
|
+
|
|
302
|
+
/** Register a one-shot listener for a Photoshop event. */
|
|
303
|
+
oncePhotoshopEvent(event: string, listener: (event: any) => void): void;
|
|
304
|
+
|
|
305
|
+
/** Remove a Photoshop event listener. */
|
|
306
|
+
removePhotoshopEventListener(event: string, listener: (event: any) => void): void;
|
|
307
|
+
|
|
308
|
+
/** List current listeners for a Photoshop event. */
|
|
309
|
+
photoshopEventListeners(event: string): Array<(event: any) => void>;
|
|
310
|
+
|
|
311
|
+
/** Emit a Photoshop event to its listeners. */
|
|
312
|
+
emitPhotoshopEvent(event: string, data?: any): void;
|
|
313
|
+
|
|
314
|
+
// --- pixmaps -------------------------------------------------------------
|
|
315
|
+
|
|
316
|
+
/** Get a pixmap (or bounds) for a layer id. */
|
|
317
|
+
getPixmap(
|
|
318
|
+
documentId: number,
|
|
319
|
+
layerSpec: number,
|
|
320
|
+
settings: PsGenerator.GetPixmapSettings
|
|
321
|
+
): Promise<PsPixmap>;
|
|
322
|
+
|
|
323
|
+
/** Get a pixmap (or bounds) for an index range of layers. */
|
|
324
|
+
getPixmap(
|
|
325
|
+
documentId: number,
|
|
326
|
+
layerSpec: {
|
|
327
|
+
firstLayerIndex: number;
|
|
328
|
+
lastLayerIndex: number;
|
|
329
|
+
hidden?: number[];
|
|
330
|
+
},
|
|
331
|
+
settings: PsGenerator.GetPixmapSettings
|
|
332
|
+
): Promise<PsPixmap>;
|
|
333
|
+
|
|
334
|
+
/** Get a pixmap of the whole document in its current visibility state. */
|
|
335
|
+
getDocumentPixmap(documentId: number, settings: PsGenerator.GetPixmapSettings): Promise<PsPixmap>;
|
|
336
|
+
|
|
337
|
+
/** Compute the `getPixmap` settings for a target scaling/padding. */
|
|
338
|
+
getPixmapParams(
|
|
339
|
+
settings: PsGenerator.PixmapParamsSettings,
|
|
340
|
+
staticInputBounds: PsBounds$1,
|
|
341
|
+
visibleInputBounds: PsBounds$1,
|
|
342
|
+
paddedInputBounds: PsBounds$1,
|
|
343
|
+
clipToBounds?: PsBounds$1
|
|
344
|
+
): PsGenerator.GetPixmapSettings;
|
|
345
|
+
|
|
346
|
+
/** Write a pixmap to `path` via ImageMagick; resolves to the written path. */
|
|
347
|
+
savePixmap(
|
|
348
|
+
pixmap: PsPixmap,
|
|
349
|
+
path: string,
|
|
350
|
+
settings: PsGenerator.SavePixmapSettings
|
|
351
|
+
): Promise<string>;
|
|
352
|
+
|
|
353
|
+
/** Stream a pixmap's converted bytes to `outputStream`. */
|
|
354
|
+
streamPixmap(
|
|
355
|
+
pixmap: PsPixmap,
|
|
356
|
+
outputStream: Stream,
|
|
357
|
+
settings: PsGenerator.SavePixmapSettings
|
|
358
|
+
): Promise<void>;
|
|
359
|
+
|
|
360
|
+
/** Parse and coerce a pixmap's numeric properties in place. */
|
|
361
|
+
_parsePixmapProperties(pixmap: PsPixmap): void;
|
|
362
|
+
|
|
363
|
+
/** Parse and coerce a save-pixmap settings object in place. */
|
|
364
|
+
_parsePixmapSaveSettings(settings: PsGenerator.SavePixmapSettings): void;
|
|
365
|
+
|
|
366
|
+
// --- shapes / svg / guides ----------------------------------------------
|
|
367
|
+
|
|
368
|
+
/** Resolve to a layer's path/shape data, or reject "layer does not contain a shape". */
|
|
369
|
+
getLayerShape(documentId: number, layerId: number): Promise<{ path: any }>;
|
|
370
|
+
|
|
371
|
+
/** Resolve to an SVG string for the layer, optionally scaled. */
|
|
372
|
+
getSVG(documentId: number, layerId: number, settings?: { scale?: number }): Promise<string>;
|
|
373
|
+
|
|
374
|
+
/** Resolve to the horizontal/vertical guide positions in a document. */
|
|
375
|
+
getGuides(documentId: number): Promise<PsGenerator.Guides>;
|
|
376
|
+
|
|
377
|
+
/** Recursively compute the containing bounds of a layer tree (or undefined). */
|
|
378
|
+
getDeepBounds(layer: any): PsBounds$1 | undefined;
|
|
379
|
+
|
|
380
|
+
// --- bounds helpers (private) -------------------------------------------
|
|
381
|
+
|
|
382
|
+
/** True when a bounds rect has zero or non-finite area. */
|
|
383
|
+
_isBoundEmpty(bounds: PsBounds$1): boolean;
|
|
384
|
+
|
|
385
|
+
/** Smallest rect containing both bounds. */
|
|
386
|
+
_unionBounds(boundsA: PsBounds$1, boundsB: PsBounds$1): PsBounds$1;
|
|
387
|
+
|
|
388
|
+
/** Largest rect inside both bounds (zeroed when empty). */
|
|
389
|
+
_intersectBounds(boundsA: PsBounds$1, boundsB: PsBounds$1): PsBounds$1;
|
|
390
|
+
|
|
391
|
+
/** Union of a layer's raster + vector mask bounds, or undefined. */
|
|
392
|
+
_getTotalMaskBounds(bounds: any): PsBounds$1 | undefined;
|
|
393
|
+
|
|
394
|
+
// --- hidden layers (private) --------------------------------------------
|
|
395
|
+
|
|
396
|
+
/** Recursively collect indices of hidden layers (children of hidden groups
|
|
397
|
+
* are hidden too). Used by `getDocumentPixmap`. */
|
|
398
|
+
_computeHiddenLayers(parent: { layers: any[] }, hideAll?: boolean): number[];
|
|
399
|
+
|
|
400
|
+
// --- style (private) -----------------------------------------------------
|
|
401
|
+
|
|
402
|
+
/** Resolve to extracted style info for a document (private, unstable API). */
|
|
403
|
+
_getStyleInfo(documentId: number, flags?: { selectedLayers?: boolean }): Promise<any>;
|
|
404
|
+
|
|
405
|
+
// --- settings parsing (private) -----------------------------------------
|
|
406
|
+
|
|
407
|
+
/** Parse a `{ json }`-wrapped settings blob into an object. */
|
|
408
|
+
_parseDocumentSettings(settings: { json?: string } | any): any;
|
|
409
|
+
|
|
410
|
+
/** Register/subscribe a Photoshop event listener helper (private). */
|
|
411
|
+
_registerPhotoshopEventHelper(
|
|
412
|
+
event: string,
|
|
413
|
+
listener: (event: any) => void,
|
|
414
|
+
isOnce: boolean
|
|
415
|
+
): void;
|
|
416
|
+
|
|
417
|
+
// --- headlights (private) -----------------------------------------------
|
|
418
|
+
|
|
419
|
+
/** Log a Headlights event (Adobe-internal). */
|
|
420
|
+
_logHeadlights(event: string): Promise<void>;
|
|
421
|
+
|
|
422
|
+
/** Log a plugin-loaded Headlights record (Adobe-internal). */
|
|
423
|
+
_logHeadlightsPluginLoaded(pluginName: string, pluginVersion: string): Promise<void>;
|
|
424
|
+
|
|
425
|
+
// --- plugin management ---------------------------------------------------
|
|
426
|
+
|
|
427
|
+
/** Read a plugin's `package.json` metadata; throws on invalid input. */
|
|
428
|
+
getPluginMetadata(directory: string): any;
|
|
429
|
+
|
|
430
|
+
/** Check a plugin's `generator-core-version` compatibility. */
|
|
431
|
+
checkPluginCompatibility(metadata: any): PsGenerator.PluginCompatibility;
|
|
432
|
+
|
|
433
|
+
/** Load a plugin from a directory; throws on incompatibility / failure. */
|
|
434
|
+
loadPlugin(directory: string): void;
|
|
435
|
+
|
|
436
|
+
/** Return an already-loaded plugin by name, or null. */
|
|
437
|
+
getPlugin(name: string): any | null;
|
|
438
|
+
|
|
439
|
+
// --- custom options ------------------------------------------------------
|
|
440
|
+
|
|
441
|
+
/** Resolve to the custom-options table for a plugin (persists until PS quit). */
|
|
442
|
+
getCustomOptions(pluginId: string): Promise<Record<string, any>>;
|
|
443
|
+
|
|
444
|
+
/** Replace the custom-options table for a plugin (no merge). */
|
|
445
|
+
setCustomOptions(pluginId: string, settings: Record<string, any>): Promise<void>;
|
|
446
|
+
|
|
447
|
+
/** Update a single custom option key for a plugin. */
|
|
448
|
+
updateCustomOption(pluginId: string, key: string, value: unknown): Promise<void>;
|
|
449
|
+
|
|
450
|
+
/** Delete a single custom option key for a plugin. */
|
|
451
|
+
deleteCustomOption(pluginId: string, key: string): Promise<void>;
|
|
452
|
+
|
|
453
|
+
// --- websocket servers (generator-core built-in) ------------------------
|
|
454
|
+
|
|
455
|
+
/** Start a generator-core websocket server for a plugin; resolves to the port. */
|
|
456
|
+
startWebsocketServer(
|
|
457
|
+
pluginId: string,
|
|
458
|
+
desiredPort?: number,
|
|
459
|
+
domain?: any,
|
|
460
|
+
origin?: string
|
|
461
|
+
): Promise<number>;
|
|
462
|
+
|
|
463
|
+
/** Stop a plugin's generator-core websocket server. */
|
|
464
|
+
stopWebsocketServer(pluginId: string): Promise<void>;
|
|
465
|
+
|
|
466
|
+
// --- interpolation constants (generator.js:1102-1135) -------------------
|
|
467
|
+
|
|
468
|
+
readonly INTERPOLATION_NEAREST_NEIGHBOR: "nearestNeighbor";
|
|
469
|
+
readonly INTERPOLATION_BILINEAR: "bilinear";
|
|
470
|
+
readonly INTERPOLATION_BICUBIC: "bicubic";
|
|
471
|
+
readonly INTERPOLATION_BICUBIC_SMOOTHER: "bicubicSmoother";
|
|
472
|
+
readonly INTERPOLATION_BICUBIC_SHARPER: "bicubicSharper";
|
|
473
|
+
readonly INTERPOLATION_BICUBIC_AUTOMATIC: "bicubicAutomatic";
|
|
474
|
+
readonly INTERPOLATION_PRESERVE_DETAILS_UPSCALE: "preserveDetailsUpscale";
|
|
475
|
+
readonly INTERPOLATION_AUTOMATIC: "automaticInterpolation";
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
interface Logger {
|
|
479
|
+
debug(message: string, ...args: unknown[]): void;
|
|
480
|
+
info(message: string, ...args: unknown[]): void;
|
|
481
|
+
warn(message: string, ...args: unknown[]): void;
|
|
482
|
+
error(message: string, ...args: unknown[]): void;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* A layer spec: either a layer id, or an index range plus the indices of layers
|
|
487
|
+
* to hide (the form Photoshop's `getLayerPixmap.jsx` accepts). Modeled here as a
|
|
488
|
+
* wire type (RFC 0008) so the protocol is self-contained; the generator's image
|
|
489
|
+
* module re-exports it for its plugin-facing API.
|
|
490
|
+
*/
|
|
491
|
+
type LayerSpec = number | {
|
|
492
|
+
firstLayerIndex: number;
|
|
493
|
+
lastLayerIndex: number;
|
|
494
|
+
hidden: number[];
|
|
495
|
+
};
|
|
496
|
+
/**
|
|
497
|
+
* The result of an image `@ws` method (RFC 0008). `data` is an out-of-the-box
|
|
498
|
+
* image string the client can drop straight into an `<img src>`:
|
|
499
|
+
* `data:image/png;base64,...` when inlined, or `https://...` when a `CosService`
|
|
500
|
+
* uploaded it. The client tells them apart by the `data`/`http` prefix — there
|
|
501
|
+
* is deliberately no separate discriminator field. `bounds`/`width`/`height`
|
|
502
|
+
* carry the same geometry as the module-internal `ImageResult`.
|
|
503
|
+
*/
|
|
504
|
+
interface WsImageResult {
|
|
505
|
+
data: string;
|
|
506
|
+
bounds: PsBounds;
|
|
507
|
+
width: number;
|
|
508
|
+
height: number;
|
|
509
|
+
}
|
|
510
|
+
interface PsBounds {
|
|
511
|
+
left: number;
|
|
512
|
+
right: number;
|
|
513
|
+
top: number;
|
|
514
|
+
bottom: number;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* The plugin contract a Plugin depends on (RFC 0003) — a deliberately narrowed
|
|
519
|
+
* view of the server's `PsBridgeHost`. Exposes only what a Plugin needs: the JSX
|
|
520
|
+
* seam and the feature-module accessors (all typed by the generator's own
|
|
521
|
+
* contract interfaces), plus the listen-only event stream. Excludes
|
|
522
|
+
* broadcast/emit/server/the raw generator handle — a Plugin manages its own
|
|
523
|
+
* clients through BasePlugin.broadcast/send.
|
|
524
|
+
*
|
|
525
|
+
* This is the one hand-written piece of the plugin surface that stays in the
|
|
526
|
+
* SDK: it is a *curation* of the host, not a mirror of it. Its member types are
|
|
527
|
+
* imported (type-only) from the generator contract so they can never drift from
|
|
528
|
+
* the implementation. The server's `PsBridgeHost implements PluginHost`;
|
|
529
|
+
* external Plugins only ever see this interface, so they depend on the SDK alone
|
|
530
|
+
* at runtime, never on the server package.
|
|
531
|
+
*/
|
|
532
|
+
interface PluginHost {
|
|
533
|
+
readonly jsx: JsxRunnerApi;
|
|
534
|
+
/** Feature modules, reached by short key (e.g. `plugin.modules.layer`). */
|
|
535
|
+
readonly modules: {
|
|
536
|
+
layer: LayerModuleApi;
|
|
537
|
+
document: DocumentModuleApi;
|
|
538
|
+
action: ActionModuleApi;
|
|
539
|
+
image: ImageModuleApi;
|
|
540
|
+
};
|
|
541
|
+
/** Typed, listen-only Photoshop event stream (lazy subscribe). */
|
|
542
|
+
readonly events: PhotoshopEvents;
|
|
543
|
+
/**
|
|
544
|
+
* Optional object-storage upload service (RFC 0008). Present only when the host
|
|
545
|
+
* has COS configured via the environment; undefined otherwise. A plugin guards
|
|
546
|
+
* on it: `if (this.plugin.cos) await this.plugin.cos.uploadObject(bytes)`.
|
|
547
|
+
*/
|
|
548
|
+
readonly cos?: CosServiceApi;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* A single progress notification emitted by Photoshop while a jsx file
|
|
553
|
+
* evaluates via `_sendJSXFile`. `type` is `"javascript"` (an evaluation result
|
|
554
|
+
* — e.g. a bounds object), `"pixmap"` (a raw pixmap buffer), or `"iccProfile"`
|
|
555
|
+
* (a raw ICC profile buffer).
|
|
556
|
+
*/
|
|
557
|
+
interface JsxProgressMessage {
|
|
558
|
+
type: "javascript" | "pixmap" | "iccProfile" | string;
|
|
559
|
+
value: unknown;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Typed handle to an in-flight `_sendJSXFile` evaluation. `onProgress`/`onFail`
|
|
563
|
+
* subscribe to the underlying deferred's streams; `resolve`/`reject` let the
|
|
564
|
+
* caller signal completion (required — the deferred won't settle on its own).
|
|
565
|
+
* Isolates the `_sendJSXFile` touchpoint inside the JSX seam so callers like
|
|
566
|
+
* `ImageModule.getPixmap` never reach generator-core's transport directly.
|
|
567
|
+
*/
|
|
568
|
+
interface JsxChannel {
|
|
569
|
+
onProgress(fn: (message: JsxProgressMessage) => void): void;
|
|
570
|
+
onFail(fn: (err: unknown) => void): void;
|
|
571
|
+
resolve(): void;
|
|
572
|
+
reject(err?: unknown): void;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* The slice of JsxRunner a Plugin reaches through `plugin.jsx` (RFC 0003 /
|
|
576
|
+
* RFC 0005). The concrete JsxRunner `implements` this (and `forPlugin` returns a
|
|
577
|
+
* scoped view that also implements it), so the plugin contract can never drift
|
|
578
|
+
* from the implementation; the SDK re-exports it (via src/contract.ts) as the
|
|
579
|
+
* type of `PluginHost.jsx`. Excludes lifecycle (`init`) and the low-level pixmap
|
|
580
|
+
* channel (`openJSXFile`) — plugins only run jsx by name (or raw string).
|
|
581
|
+
*
|
|
582
|
+
* `execute` resolves against *this handle's own* jsx scope: the built-in `jsx/`
|
|
583
|
+
* tree for the host's root runner, or the plugin's own `jsx/` dir for the scoped
|
|
584
|
+
* view `plugin.jsx` returns. `executeBuiltin` always targets the built-in tree,
|
|
585
|
+
* so a plugin can reach a host domain (e.g. `Document/getDocumentInfo`) without
|
|
586
|
+
* knowing its own id or dir. `run` takes a raw script and is scope-independent.
|
|
587
|
+
*/
|
|
588
|
+
interface JsxRunnerApi {
|
|
589
|
+
execute<T = unknown>(name: string, params?: Record<string, unknown>, sharedEngineSafe?: boolean): Promise<T>;
|
|
590
|
+
executeBuiltin<T = unknown>(name: string, params?: Record<string, unknown>, sharedEngineSafe?: boolean): Promise<T>;
|
|
591
|
+
run<T = unknown>(script: string): Promise<T>;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Runs packaged jsx by name (ADR 0008). Resolves a name like
|
|
595
|
+
* `Document/getDocumentInfo` to its physical path under the bundle's `jsx/`
|
|
596
|
+
* directory, hands it to the injected generator's `evaluateJSXFile`, and
|
|
597
|
+
* normalizes the result.
|
|
598
|
+
*
|
|
599
|
+
* Returns `message.value` verbatim — it does NOT `JSON.parse` for the caller;
|
|
600
|
+
* the `T` type parameter is a labelling convenience only. A string value
|
|
601
|
+
* starting with `"Error:"` becomes a thrown `Error` carrying the remainder.
|
|
602
|
+
*
|
|
603
|
+
* jsx text caching is handled by generator-core's `_sendJSXCache`, so this seam
|
|
604
|
+
* adds none of its own. `__dirname` is `dist` for all bundled code, so
|
|
605
|
+
* `polyfillsDir` defaults to `dist/jsx/polyfills`.
|
|
606
|
+
*
|
|
607
|
+
* ExtendScript's default engine persists globals across evaluations, so the
|
|
608
|
+
* polyfills injected once in `init()` remain available to every later
|
|
609
|
+
* `execute` (and to `run` when it uses the default engine). The default engine
|
|
610
|
+
* is the only one primed — `sharedEngineSafe` callers use a separate engine
|
|
611
|
+
* that does NOT receive the polyfills.
|
|
612
|
+
*/
|
|
613
|
+
declare class JsxRunner implements JsxRunnerApi {
|
|
614
|
+
private readonly generator;
|
|
615
|
+
private readonly logger;
|
|
616
|
+
private readonly polyfillsDir;
|
|
617
|
+
private readonly jsxDir;
|
|
618
|
+
private polyfillsCache;
|
|
619
|
+
constructor(generator: PsGenerator, logger: Logger, polyfillsDir?: string);
|
|
620
|
+
/**
|
|
621
|
+
* A jsx runner scoped to a plugin's own `jsx/` dir (RFC 0005). The returned
|
|
622
|
+
* handle's `execute` resolves under `dir`, while `executeBuiltin` still targets
|
|
623
|
+
* the built-in tree; `run` is unchanged. The scope is the *only* per-plugin
|
|
624
|
+
* state — there is no shared mutable registry; the host builds one of these per
|
|
625
|
+
* plugin in `hostFor` and injects it as `plugin.jsx`.
|
|
626
|
+
*/
|
|
627
|
+
forPlugin(dir: string): JsxRunnerApi;
|
|
628
|
+
/**
|
|
629
|
+
* Resolve a jsx name to an absolute `.jsx` path under `baseDir`. The name may
|
|
630
|
+
* carry domain subdirs (e.g. `Document/getDocumentInfo`). No escape guard: jsx
|
|
631
|
+
* names come from trusted in-process code (a module or a plugin's own source),
|
|
632
|
+
* which already runs arbitrary JS, so a guard would add no boundary.
|
|
633
|
+
*/
|
|
634
|
+
private resolvePath;
|
|
635
|
+
/**
|
|
636
|
+
* Prime the default ExtendScript engine with ES polyfills. Reads every
|
|
637
|
+
* `*.js` file under `polyfillsDir` (recursively, sorted by relative path for
|
|
638
|
+
* deterministic concatenation order), concatenates them into `polyfillsCache`,
|
|
639
|
+
* and evaluates the bundle once. Must be awaited before any `execute` call
|
|
640
|
+
* that depends on the polyfills; `PsBridgeHost.onInit` does this before
|
|
641
|
+
* `server.listen`.
|
|
642
|
+
*
|
|
643
|
+
* Missing dir -> throw (packaging bug). Empty dir -> no-op. Injection
|
|
644
|
+
* returning `"Error:…"` or rejecting -> throw, so a broken polyfill surfaces
|
|
645
|
+
* at startup rather than as a runtime `find is not a function`.
|
|
646
|
+
*/
|
|
647
|
+
init(): Promise<void>;
|
|
648
|
+
/**
|
|
649
|
+
* Run the jsx registered under `name` in the built-in `jsx/` tree (domain
|
|
650
|
+
* subdirs included, e.g. `Document/getDocumentInfo`). `params` are inlined into
|
|
651
|
+
* the script; `sharedEngineSafe` opts into Photoshop's shared script engine.
|
|
652
|
+
* The root runner's own scope *is* the built-in tree, so `execute` and
|
|
653
|
+
* `executeBuiltin` coincide here; a plugin's scoped view (see `forPlugin`)
|
|
654
|
+
* splits them apart.
|
|
655
|
+
*/
|
|
656
|
+
execute<T = unknown>(name: string, params?: Record<string, unknown>, sharedEngineSafe?: boolean): Promise<T>;
|
|
657
|
+
/**
|
|
658
|
+
* Alias of `execute` on the root runner — always the built-in tree. Present so
|
|
659
|
+
* the root satisfies `JsxRunnerApi` alongside the scoped view; the scoped view
|
|
660
|
+
* overrides `execute` (plugin dir) while delegating `executeBuiltin` here.
|
|
661
|
+
*/
|
|
662
|
+
executeBuiltin<T = unknown>(name: string, params?: Record<string, unknown>, sharedEngineSafe?: boolean): Promise<T>;
|
|
663
|
+
/**
|
|
664
|
+
* Run the jsx under `name` resolved against `baseDir`. The seam through which
|
|
665
|
+
* both the root runner (built-in tree) and the scoped view (a plugin's dir)
|
|
666
|
+
* reach Photoshop; keeps path resolution + result normalization in one place.
|
|
667
|
+
*
|
|
668
|
+
* @internal Server-internal — not on `JsxRunnerApi`, not reachable by plugins.
|
|
669
|
+
* Public only so the sibling `ScopedJsx` can delegate to it.
|
|
670
|
+
*/
|
|
671
|
+
executeIn<T = unknown>(baseDir: string, name: string, params?: Record<string, unknown>, sharedEngineSafe?: boolean): Promise<T>;
|
|
672
|
+
/**
|
|
673
|
+
* Evaluate an arbitrary jsx string in the default ExtendScript engine (the
|
|
674
|
+
* same engine `init()` primed with polyfills). No `sharedEngineSafe` opt-out:
|
|
675
|
+
* polyfills live only in the default engine, so a shared-engine variant would
|
|
676
|
+
* be silently un-polyfilled. Return value follows the same convention as
|
|
677
|
+
* `run` — verbatim, with `"Error:"`-prefixed strings turned into thrown
|
|
678
|
+
* Errors.
|
|
679
|
+
*/
|
|
680
|
+
run<T = unknown>(script: string): Promise<T>;
|
|
681
|
+
/**
|
|
682
|
+
* Open a built-in packaged jsx file via the low-level `_sendJSXFile` channel
|
|
683
|
+
* and return a typed handle to its in-flight evaluation. A root-runner-only
|
|
684
|
+
* seam (not on `JsxRunnerApi`): the only caller is `ImageModule.getPixmap`.
|
|
685
|
+
* Plugins reach pixmaps through `plugin.modules.image`, not this channel.
|
|
686
|
+
*
|
|
687
|
+
* Unlike `run` (which
|
|
688
|
+
* awaits a single resolved value), this exposes the raw progress stream so
|
|
689
|
+
* the caller can collect the multi-message responses Photoshop emits for
|
|
690
|
+
* pixmap-producing scripts (bounds + pixmap + optional ICC profile). The
|
|
691
|
+
* caller owns completion: it must call `channel.resolve()` once it has
|
|
692
|
+
* received every message it expected, or `channel.reject(err)` on failure.
|
|
693
|
+
*
|
|
694
|
+
* `sharedEngineSafe` defaults to `true` to match the pixmap protocol
|
|
695
|
+
* (generator-core's `getPixmap` / `getDocumentPixmap` both use the shared
|
|
696
|
+
* engine).
|
|
697
|
+
*/
|
|
698
|
+
openJSXFile(name: string, params?: Record<string, unknown>, sharedEngineSafe?: boolean): JsxChannel;
|
|
699
|
+
/**
|
|
700
|
+
* Shared result normalization for `run` and `execute`: a string starting with
|
|
701
|
+
* `"Error:"` becomes a thrown `Error` carrying the remainder; everything else
|
|
702
|
+
* is returned verbatim (no `JSON.parse` — `T` is a labelling convenience).
|
|
703
|
+
*/
|
|
704
|
+
private normalizeJsxResult;
|
|
705
|
+
/**
|
|
706
|
+
* Recursively list `*.js` files under `polyfillsDir`, sorted by relative path
|
|
707
|
+
* (POSIX-normalized) so concatenation order is stable across platforms and
|
|
708
|
+
* polyfills that depend on each other load in a fixed sequence. Throws if the
|
|
709
|
+
* directory itself is missing.
|
|
710
|
+
*/
|
|
711
|
+
private collectPolyfillFiles;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Layer/document bounds in pixels. The console logged this as `[Object]` (not
|
|
716
|
+
* expanded); this is the standard generator bounds shape.
|
|
717
|
+
*/
|
|
718
|
+
interface Bounds {
|
|
719
|
+
top: number;
|
|
720
|
+
left: number;
|
|
721
|
+
bottom: number;
|
|
722
|
+
right: number;
|
|
723
|
+
}
|
|
724
|
+
/** One entry in `imageChanged.layers` — a layer touched by the change. */
|
|
725
|
+
interface ImageChangedLayer {
|
|
726
|
+
/** Layer id (stable across the session). */
|
|
727
|
+
id: number;
|
|
728
|
+
/** Present (true) when the layer's pixels changed. */
|
|
729
|
+
pixels?: boolean;
|
|
730
|
+
/** Present (true) when the layer was removed in this change. */
|
|
731
|
+
removed?: boolean;
|
|
732
|
+
/** Layer bounds; present on geometry/pixel changes. */
|
|
733
|
+
bounds?: Bounds;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Payload of the `imageChanged` event. Fields are a union of everything PS may
|
|
737
|
+
* send; only `version` / `timeStamp` / `count` / `id` are always present. A
|
|
738
|
+
* single event carries *either* metadata flags (`metaDataOnly`, `selection`),
|
|
739
|
+
* *or* `layers`, *or* document-level flags (`active` / `file` / `closed`).
|
|
740
|
+
*/
|
|
741
|
+
interface ImageChangedEvent {
|
|
742
|
+
/** Generator protocol version, e.g. "1.6.1". */
|
|
743
|
+
version: string;
|
|
744
|
+
/** Seconds since epoch (float), e.g. 1782455135.936. */
|
|
745
|
+
timeStamp: number;
|
|
746
|
+
/** Per-document monotonically increasing change counter (resets per doc id). */
|
|
747
|
+
count: number;
|
|
748
|
+
/** Document id this change belongs to. */
|
|
749
|
+
id: number;
|
|
750
|
+
/** True on the first event for a doc / on activation. */
|
|
751
|
+
active?: boolean;
|
|
752
|
+
/** Document title or full path, e.g. "Test-恢复的.psd" or "C:\\...\\Test.psd". */
|
|
753
|
+
file?: string;
|
|
754
|
+
/** True when the document was closed. */
|
|
755
|
+
closed?: boolean;
|
|
756
|
+
/** True when only metadata changed (no pixel/layer body). */
|
|
757
|
+
metaDataOnly?: boolean;
|
|
758
|
+
/** Selected layer indices; empty array when the selection is cleared. */
|
|
759
|
+
selection?: number[];
|
|
760
|
+
/** Layers touched by this change (pixel edits, bounds, removals). */
|
|
761
|
+
layers?: ImageChangedLayer[];
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Map of Photoshop event name -> payload type passed to the listener. Shapes
|
|
765
|
+
* marked "observed" were confirmed from live PS output; the rest are inferred
|
|
766
|
+
* from the generator protocol docs.
|
|
767
|
+
*/
|
|
768
|
+
interface PhotoshopEventMap {
|
|
769
|
+
/** [workspace display name] (inferred). */
|
|
770
|
+
workspaceChanged: string;
|
|
771
|
+
/** Tool name, e.g. "paintbrushTool" / "moveTool" (observed). */
|
|
772
|
+
toolChanged: string;
|
|
773
|
+
/** "enter" | "exit" (inferred). */
|
|
774
|
+
quickMaskStateChanged: string;
|
|
775
|
+
/** Document id (observed). */
|
|
776
|
+
documentChanged: number;
|
|
777
|
+
/** Document id of the closed document (observed). */
|
|
778
|
+
closedDocument: number;
|
|
779
|
+
/** Document id (inferred). */
|
|
780
|
+
newDocumentViewCreated: number;
|
|
781
|
+
/** Document id (inferred). */
|
|
782
|
+
activeViewChanged: number;
|
|
783
|
+
/** Document id of the now-current document (observed). */
|
|
784
|
+
currentDocumentChanged: number;
|
|
785
|
+
/** Color as 6-character hex value (inferred). */
|
|
786
|
+
backgroundColorChanged: string;
|
|
787
|
+
/** Color as 6-character hex value (inferred). */
|
|
788
|
+
foregroundColorChanged: string;
|
|
789
|
+
/** Image/document change descriptor (observed). */
|
|
790
|
+
imageChanged: ImageChangedEvent;
|
|
791
|
+
}
|
|
792
|
+
/** Listener for a given Photoshop event key. */
|
|
793
|
+
type PhotoshopEventListener<K extends keyof PhotoshopEventMap> = (payload: PhotoshopEventMap[K]) => void;
|
|
794
|
+
/**
|
|
795
|
+
* Listen-only typed surface a Plugin reaches through `plugin.events` /
|
|
796
|
+
* `this.events`. `EventManager` (an `EventEmitter`) `implements` this; `emit` is
|
|
797
|
+
* deliberately excluded — a Plugin subscribes, it never dispatches Photoshop
|
|
798
|
+
* events.
|
|
799
|
+
*/
|
|
800
|
+
interface PhotoshopEvents {
|
|
801
|
+
on<K extends keyof PhotoshopEventMap>(event: K, listener: PhotoshopEventListener<K>): this;
|
|
802
|
+
once<K extends keyof PhotoshopEventMap>(event: K, listener: PhotoshopEventListener<K>): this;
|
|
803
|
+
off<K extends keyof PhotoshopEventMap>(event: K, listener: PhotoshopEventListener<K>): this;
|
|
804
|
+
}
|
|
805
|
+
type Listener<K extends keyof PhotoshopEventMap> = PhotoshopEventListener<K>;
|
|
806
|
+
/**
|
|
807
|
+
* Owns the plugin's Photoshop event subscriptions, exposed as a typed
|
|
808
|
+
* `EventEmitter`. Held by `PsBridgeHost` (see `plugin.events`).
|
|
809
|
+
*
|
|
810
|
+
* Subscriptions are lazy: the manager only calls `generator.onPhotoshopEvent`
|
|
811
|
+
* the first time a caller listens to an event, and `removePhotoshopEventListener`
|
|
812
|
+
* once the last listener for that event goes away. Reference counting rides on
|
|
813
|
+
* the `newListener` / `removeListener` meta-events so every add/remove path
|
|
814
|
+
* (`on` / `once` / `off` / `removeAllListeners`) is covered.
|
|
815
|
+
*
|
|
816
|
+
* The `on` / `once` / `off` / `emit` signatures are narrowed to
|
|
817
|
+
* `PhotoshopEventMap`, so only confirmed events can be listened to at compile
|
|
818
|
+
* time; unknown names throw at runtime.
|
|
819
|
+
*/
|
|
820
|
+
declare class EventManager extends EventEmitter implements PhotoshopEvents {
|
|
821
|
+
private readonly generator;
|
|
822
|
+
private readonly bridges;
|
|
823
|
+
constructor(generator: PsGenerator);
|
|
824
|
+
on<K extends keyof PhotoshopEventMap>(event: K, listener: Listener<K>): this;
|
|
825
|
+
once<K extends keyof PhotoshopEventMap>(event: K, listener: Listener<K>): this;
|
|
826
|
+
addListener<K extends keyof PhotoshopEventMap>(event: K, listener: Listener<K>): this;
|
|
827
|
+
off<K extends keyof PhotoshopEventMap>(event: K, listener: Listener<K>): this;
|
|
828
|
+
removeListener<K extends keyof PhotoshopEventMap>(event: K, listener: Listener<K>): this;
|
|
829
|
+
/** Dispatch a payload to listeners. Fired by the PS bridge; not for external use. */
|
|
830
|
+
emit<K extends keyof PhotoshopEventMap>(event: K, payload: PhotoshopEventMap[K]): boolean;
|
|
831
|
+
emit(event: string | symbol, ...args: unknown[]): boolean;
|
|
832
|
+
/** First listener for a PS event -> subscribe upstream once. */
|
|
833
|
+
private onAdd;
|
|
834
|
+
/** Last listener for a PS event removed -> detach the upstream bridge. */
|
|
835
|
+
private onRemove;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Action-domain feature module (ADR 0006). Exposes the `Action:*` WS Request
|
|
840
|
+
* methods, each backed by a packaged `jsx/Action/<name>.jsx` script run through
|
|
841
|
+
* the plugin's `JsxRunner` (ADR 0008). A jsx failure follows the `"Error:"`
|
|
842
|
+
* prefix convention: `JsxRunner` throws, and `Registry.dispatch` turns it into an
|
|
843
|
+
* INTERNAL response — the methods themselves do not catch.
|
|
844
|
+
*
|
|
845
|
+
* Migrated from LightAi's `ActionManager`. The `@McpTool` metadata did not carry
|
|
846
|
+
* over (no MCP runtime here — only the `@ws` WS path), but the human-facing
|
|
847
|
+
* descriptions are preserved as method JSDoc.
|
|
848
|
+
*/
|
|
849
|
+
/**
|
|
850
|
+
* The Action module surface a Plugin reaches through `plugin.modules.action`
|
|
851
|
+
* (RFC 0003). `ActionModule implements` this; the SDK re-exports it via
|
|
852
|
+
* src/contract.ts.
|
|
853
|
+
*/
|
|
854
|
+
interface ActionModuleApi {
|
|
855
|
+
autoCutout(): Promise<boolean>;
|
|
856
|
+
removeBackground(): Promise<{
|
|
857
|
+
success: boolean;
|
|
858
|
+
}>;
|
|
859
|
+
}
|
|
860
|
+
declare class ActionModule extends BaseModule implements ActionModuleApi {
|
|
861
|
+
constructor(plugin: PsBridgeHost);
|
|
862
|
+
/**
|
|
863
|
+
* Automatically create a selection for the main subject of the current layer.
|
|
864
|
+
* Runs `jsx/Action/autoCutout.jsx`. The jsx return value is not consulted:
|
|
865
|
+
* success is implicit, and a failure surfaces as a thrown error (hence an
|
|
866
|
+
* INTERNAL response), so this always resolves to `true` on the happy path.
|
|
867
|
+
*/
|
|
868
|
+
autoCutout(): Promise<boolean>;
|
|
869
|
+
/**
|
|
870
|
+
* Remove the background of the current layer. Runs `jsx/Action/removeBackground.jsx`
|
|
871
|
+
* and wraps the jsx's boolean result as `{ success }`.
|
|
872
|
+
*/
|
|
873
|
+
removeBackground(): Promise<{
|
|
874
|
+
success: boolean;
|
|
875
|
+
}>;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
type PsDocument = {
|
|
879
|
+
id: number;
|
|
880
|
+
name: string;
|
|
881
|
+
width: number;
|
|
882
|
+
height: number;
|
|
883
|
+
resolution: number;
|
|
884
|
+
isDirty: boolean;
|
|
885
|
+
filePath?: string;
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* The Document module surface a Plugin reaches through `plugin.modules.document`
|
|
889
|
+
* (RFC 0003). `DocumentModule implements` this; the SDK re-exports it via
|
|
890
|
+
* src/contract.ts.
|
|
891
|
+
*/
|
|
892
|
+
interface DocumentModuleApi {
|
|
893
|
+
getCurrentDocument(): Promise<PsDocument>;
|
|
894
|
+
exportDocument(params: Record<string, unknown>): Promise<unknown>;
|
|
895
|
+
saveDocument(params: {
|
|
896
|
+
savePath?: string;
|
|
897
|
+
}): Promise<unknown>;
|
|
898
|
+
}
|
|
899
|
+
declare class DocumentModule extends BaseModule implements DocumentModuleApi {
|
|
900
|
+
constructor(plugin: PsBridgeHost);
|
|
901
|
+
currentDocument: PsDocument | null;
|
|
902
|
+
getCurrentDocument(): Promise<PsDocument>;
|
|
903
|
+
exportDocument(params: Record<string, any>): Promise<unknown>;
|
|
904
|
+
saveDocument(params: {
|
|
905
|
+
savePath?: string;
|
|
906
|
+
}): Promise<unknown>;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Single-layer pixmap export + preview, isolated from the buggy
|
|
911
|
+
* `generator.getPixmap` (generator-core's version omits the
|
|
912
|
+
* `includeAdjustors/Children/ClipBase/Clipped` flags and passes a stray
|
|
913
|
+
* `settings.thread` field). `getPixmap` here is a faithful port of
|
|
914
|
+
* LightAi's `LayerManager.getPixmap` (index.ts:364-482): it calls the plugin's
|
|
915
|
+
* own `Layer/getLayerPixmap.jsx` over the progress channel and collects the
|
|
916
|
+
* bounds + pixmap + ICC profile messages Photoshop streams back.
|
|
917
|
+
*
|
|
918
|
+
* Whole-document export is handled separately by generator-core's
|
|
919
|
+
* `getDocumentPixmap` (to be wired up later); this module only deals with
|
|
920
|
+
* explicit layer specs, which is what `Layer/getLayerPixmap.jsx` requires.
|
|
921
|
+
*
|
|
922
|
+
* Encoding (raw RGBA -> PNG) goes through `sharp`, externalized from the bundle
|
|
923
|
+
* and resolved from node_modules at runtime inside Photoshop's Node.
|
|
924
|
+
*/
|
|
925
|
+
/**
|
|
926
|
+
* The Image module surface a Plugin reaches through `plugin.modules.image`
|
|
927
|
+
* (RFC 0003). `ImageModule implements` this; the SDK re-exports it via
|
|
928
|
+
* src/contract.ts. `settings` is widened to `Record<string, unknown>` so the
|
|
929
|
+
* plugin contract does not drag the generator-core `GetPixmapSettings` namespace
|
|
930
|
+
* into the SDK; `buffer` is `Uint8Array` (not `Buffer`) so the SDK stays
|
|
931
|
+
* Node-free.
|
|
932
|
+
*/
|
|
933
|
+
interface ImageModuleApi {
|
|
934
|
+
exportImage(options: {
|
|
935
|
+
documentId?: number;
|
|
936
|
+
layerSpec: LayerSpec;
|
|
937
|
+
settings?: Record<string, unknown>;
|
|
938
|
+
}): Promise<ImageResult>;
|
|
939
|
+
getPreview(options: {
|
|
940
|
+
documentId?: number;
|
|
941
|
+
layerSpec: number;
|
|
942
|
+
}): Promise<ImageResult>;
|
|
943
|
+
exportDocument(options: {
|
|
944
|
+
documentId?: number;
|
|
945
|
+
settings?: Record<string, unknown>;
|
|
946
|
+
}): Promise<ImageResult>;
|
|
947
|
+
}
|
|
948
|
+
declare class ImageModule extends BaseModule implements ImageModuleApi {
|
|
949
|
+
constructor(plugin: PsBridgeHost);
|
|
950
|
+
/**
|
|
951
|
+
* Export a single layer as a PNG buffer plus its bounds and pixel
|
|
952
|
+
* dimensions. `settings` carries the `GetPixmapSettings` Photoshop accepts;
|
|
953
|
+
* the four `include*` flags default to `true` when unspecified (the fix over
|
|
954
|
+
* generator-core). `layerSpec` is required — the underlying jsx needs an
|
|
955
|
+
* explicit layer id or index range.
|
|
956
|
+
*/
|
|
957
|
+
exportImage(options: {
|
|
958
|
+
documentId?: number;
|
|
959
|
+
layerSpec: LayerSpec;
|
|
960
|
+
settings?: PsGenerator.GetPixmapSettings;
|
|
961
|
+
}): Promise<ImageResult>;
|
|
962
|
+
/**
|
|
963
|
+
* Export a downscaled preview of a single layer. The scale is computed so
|
|
964
|
+
* the longer edge lands near 300px; scaling is done by Photoshop via
|
|
965
|
+
* `scaleX/scaleY`, not by `sharp`. `includeClipped/ClipBase/Adjustors` are
|
|
966
|
+
* forced to `false` to fetch only the body layer's pixels. `layerSpec` is
|
|
967
|
+
* required (a layer id; the layer's `rect` drives the scale).
|
|
968
|
+
*/
|
|
969
|
+
getPreview(options: {
|
|
970
|
+
documentId?: number;
|
|
971
|
+
layerSpec: number;
|
|
972
|
+
}): Promise<ImageResult>;
|
|
973
|
+
/**
|
|
974
|
+
* Export the whole document (its current visibility state, flattened) as a PNG
|
|
975
|
+
* buffer plus bounds and pixel dimensions. Unlike `exportImage` (a single layer
|
|
976
|
+
* via the `Layer/getLayerPixmap` jsx protocol), this uses generator-core's
|
|
977
|
+
* built-in `getDocumentPixmap`, which returns an already-parsed `PsPixmap`.
|
|
978
|
+
* `documentId` defaults to the current document; `settings` carries the
|
|
979
|
+
* `GetPixmapSettings` Photoshop accepts (e.g. `scaleX`/`scaleY`).
|
|
980
|
+
*/
|
|
981
|
+
exportDocument(options: {
|
|
982
|
+
documentId?: number;
|
|
983
|
+
settings?: PsGenerator.GetPixmapSettings;
|
|
984
|
+
}): Promise<ImageResult>;
|
|
985
|
+
/**
|
|
986
|
+
* `@ws` wrapper over {@link exportImage} (RFC 0008). Returns a wire-friendly
|
|
987
|
+
* {@link WsImageResult}: when `plugin.cos` is enabled the PNG is uploaded and
|
|
988
|
+
* `data` is an https URL, otherwise `data` is a base64 data URI. A COS upload
|
|
989
|
+
* failure throws (no base64 fallback) — a configured channel must be used.
|
|
990
|
+
*/
|
|
991
|
+
exportLayerWs(options: {
|
|
992
|
+
documentId?: number;
|
|
993
|
+
layerSpec: LayerSpec;
|
|
994
|
+
settings?: PsGenerator.GetPixmapSettings;
|
|
995
|
+
}): Promise<WsImageResult>;
|
|
996
|
+
/**
|
|
997
|
+
* `@ws` wrapper over {@link getPreview} (RFC 0008). Always returns base64 —
|
|
998
|
+
* previews are high-frequency, downscaled thumbnails not worth a COS round-trip,
|
|
999
|
+
* so this never uploads even when `plugin.cos` is enabled.
|
|
1000
|
+
*/
|
|
1001
|
+
getPreviewWs(options: {
|
|
1002
|
+
documentId?: number;
|
|
1003
|
+
layerSpec: number;
|
|
1004
|
+
}): Promise<WsImageResult>;
|
|
1005
|
+
/**
|
|
1006
|
+
* `@ws` wrapper over {@link exportDocument} (RFC 0008). Uploads to COS when
|
|
1007
|
+
* enabled (https URL), else base64; a COS failure throws.
|
|
1008
|
+
*/
|
|
1009
|
+
exportDocumentWs(options: {
|
|
1010
|
+
documentId?: number;
|
|
1011
|
+
settings?: PsGenerator.GetPixmapSettings;
|
|
1012
|
+
}): Promise<WsImageResult>;
|
|
1013
|
+
/**
|
|
1014
|
+
* Turn a module-internal {@link ImageResult} (raw PNG `buffer`) into the
|
|
1015
|
+
* wire-friendly {@link WsImageResult} (`data` string). With `upload` set and
|
|
1016
|
+
* `plugin.cos` enabled, the buffer is uploaded and `data` is the signed URL;
|
|
1017
|
+
* otherwise `data` is a `data:image/png;base64,...` URI. Both forms drop
|
|
1018
|
+
* straight into an `<img src>`.
|
|
1019
|
+
*/
|
|
1020
|
+
private toWsResult;
|
|
1021
|
+
/**
|
|
1022
|
+
* Resolve a layer's name for the COS object key. A numeric `layerSpec` is
|
|
1023
|
+
* looked up via the layer module; an index-range spec has no single name, so it
|
|
1024
|
+
* falls back to "layers". Lookup failures degrade to `layer-{id}` rather than
|
|
1025
|
+
* failing the export.
|
|
1026
|
+
*/
|
|
1027
|
+
private resolveLayerName;
|
|
1028
|
+
/**
|
|
1029
|
+
* Resolve a document's name for the COS object key. Uses the current document's
|
|
1030
|
+
* name when the target is the current document; otherwise falls back to
|
|
1031
|
+
* `doc-{id}` rather than spending a jsx round-trip to name an off-screen doc.
|
|
1032
|
+
*/
|
|
1033
|
+
private resolveDocumentName;
|
|
1034
|
+
/**
|
|
1035
|
+
* Faithful port of LightAi `LayerManager.getPixmap` (index.ts:364-482).
|
|
1036
|
+
* Builds the params (dropping generator-core's stray `settings.thread` and
|
|
1037
|
+
* defaulting the four `include*` flags to `true`), opens the pixmap jsx over
|
|
1038
|
+
* the progress channel, and resolves three native promises as the
|
|
1039
|
+
* bounds/pixmap/iccProfile messages arrive. Once all expected messages are
|
|
1040
|
+
* in, signals the channel to settle and constructs a `Pixmap`.
|
|
1041
|
+
*/
|
|
1042
|
+
private getPixmap;
|
|
1043
|
+
/**
|
|
1044
|
+
* Resolve the document id: explicit override, else the document module's
|
|
1045
|
+
* current document, else fail loud (matches LightAi's "No document opened").
|
|
1046
|
+
*/
|
|
1047
|
+
private resolveDocumentId;
|
|
1048
|
+
/**
|
|
1049
|
+
* Convert a pixmap's raw pixels into a tightly-packed RGBA buffer. Handles both
|
|
1050
|
+
* the single-layer protocol (`getLayerPixmap`: 4-channel, no row padding) and
|
|
1051
|
+
* generator-core's `getDocumentPixmap` (which may return 3-channel pixmaps and
|
|
1052
|
+
* rows padded to `rowBytes`). 4-channel is Photoshop's `[A,R,G,B]` layout;
|
|
1053
|
+
* 3-channel is `[R,G,B]` and gets an opaque alpha. Rows are walked by
|
|
1054
|
+
* `rowBytes` so any per-row padding is skipped rather than misaligning the image.
|
|
1055
|
+
*/
|
|
1056
|
+
private parseRawPixels;
|
|
1057
|
+
private encodePng;
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Result of an image export / preview: PNG bytes plus geometry metadata. The
|
|
1061
|
+
* bytes are typed as `Uint8Array` (the implementation returns a `Buffer`, which
|
|
1062
|
+
* is a `Uint8Array` subtype) so this type can cross into the Node-free SDK
|
|
1063
|
+
* contract unchanged.
|
|
1064
|
+
*/
|
|
1065
|
+
interface ImageResult {
|
|
1066
|
+
buffer: Uint8Array;
|
|
1067
|
+
bounds: PsBounds$1;
|
|
1068
|
+
width: number;
|
|
1069
|
+
height: number;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Plugin-facing COS contract (RFC 0008). The minimal slice modules and plugins
|
|
1074
|
+
* reach through `plugin.cos`: upload in-memory bytes or a local file and get back
|
|
1075
|
+
* a ready-to-use signed URL. Params use `Uint8Array`/path strings (never `Buffer`
|
|
1076
|
+
* or the COS SDK's own types) so the SDK's re-export of this interface stays
|
|
1077
|
+
* Node-free, mirroring how `ImageModuleApi` is exposed.
|
|
1078
|
+
*/
|
|
1079
|
+
interface CosServiceApi {
|
|
1080
|
+
/** Upload raw bytes, returning a signed URL. `name` labels the object key. */
|
|
1081
|
+
uploadObject(data: Uint8Array, name?: string): Promise<string>;
|
|
1082
|
+
/** Upload a local file by path, returning a signed URL. */
|
|
1083
|
+
uploadFile(dir: string, name?: string): Promise<string>;
|
|
1084
|
+
}
|
|
1085
|
+
/** Permanent-key COS config, read from the environment by {@link CosService.fromEnv}. */
|
|
1086
|
+
interface CosConfig {
|
|
1087
|
+
secretId: string;
|
|
1088
|
+
secretKey: string;
|
|
1089
|
+
bucket: string;
|
|
1090
|
+
region: string;
|
|
1091
|
+
keyPrefix: string;
|
|
1092
|
+
urlExpires: number;
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Optional object-storage upload unit (RFC 0008). Enabled only when the four
|
|
1096
|
+
* `PS_BRIDGE_COS_*` env fields are present; otherwise the host leaves `plugin.cos`
|
|
1097
|
+
* undefined and image exports fall back to base64. Uses a permanent key pair and
|
|
1098
|
+
* returns signed URLs without an attachment disposition, so the image they point
|
|
1099
|
+
* at stays inline-displayable.
|
|
1100
|
+
*/
|
|
1101
|
+
declare class CosService implements CosServiceApi {
|
|
1102
|
+
private readonly config;
|
|
1103
|
+
private readonly logger;
|
|
1104
|
+
private readonly cos;
|
|
1105
|
+
constructor(config: CosConfig, logger: Logger);
|
|
1106
|
+
/**
|
|
1107
|
+
* Build a CosService from the environment, or return undefined when COS is not
|
|
1108
|
+
* configured. All four `PS_BRIDGE_COS_SECRET_ID/SECRET_KEY/BUCKET/REGION` must be
|
|
1109
|
+
* present and non-empty — a missing field means "not enabled", decided once at
|
|
1110
|
+
* startup rather than failing loudly on the first upload.
|
|
1111
|
+
*/
|
|
1112
|
+
static fromEnv(logger: Logger): CosService | undefined;
|
|
1113
|
+
uploadObject(data: Uint8Array, name?: string): Promise<string>;
|
|
1114
|
+
uploadFile(dir: string, name?: string): Promise<string>;
|
|
1115
|
+
/**
|
|
1116
|
+
* Compose the object key `{keyPrefix}/{name}-{ts}{ext}` (keyPrefix is env-
|
|
1117
|
+
* configurable, default `ps-bridge/exports`). `name` (a layer/document name) is
|
|
1118
|
+
* kept verbatim including non-ASCII (e.g. Chinese); only path separators and
|
|
1119
|
+
* whitespace are replaced — they would nest the key or break the URL — and the
|
|
1120
|
+
* label is length-capped. Uniqueness rides on the timestamp, not the label.
|
|
1121
|
+
*/
|
|
1122
|
+
private buildKey;
|
|
1123
|
+
private sanitizeName;
|
|
1124
|
+
private putObject;
|
|
1125
|
+
private putFile;
|
|
1126
|
+
private signedUrl;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/** Host config handed in by generator-core (self._config[name]). */
|
|
1130
|
+
interface PluginConfig {
|
|
1131
|
+
port?: number;
|
|
1132
|
+
/**
|
|
1133
|
+
* Directory whose direct child folders are loaded as plugin packages
|
|
1134
|
+
* (each a `package.json` with a `main` entry; see the plugin loader).
|
|
1135
|
+
* Defaults to `<generator-package>/plugins` — i.e. `packages/generator/plugins`,
|
|
1136
|
+
* a symlink to the repo-root `/plugins` in development.
|
|
1137
|
+
*/
|
|
1138
|
+
pluginsDir?: string;
|
|
1139
|
+
[key: string]: unknown;
|
|
1140
|
+
}
|
|
1141
|
+
/**
|
|
1142
|
+
* Test-only overrides for `JsxRunner` construction. Production callers pass
|
|
1143
|
+
* nothing; tests point `polyfillsDir` at the source `jsx/polyfills` tree so
|
|
1144
|
+
* `init()` reads real files instead of the bundler's `dist/jsx/polyfills`
|
|
1145
|
+
* (which `__dirname`-based resolution can't reach under vitest's source
|
|
1146
|
+
* runtime).
|
|
1147
|
+
*/
|
|
1148
|
+
interface JsxRunnerOverrides {
|
|
1149
|
+
polyfillsDir?: string;
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* The host generator-core loads. Registers a menu item and starts the server's
|
|
1153
|
+
* own WebSocket service (ADR 0003). Every call into Photoshop goes through the
|
|
1154
|
+
* injected `generator`, which makes the whole init path observable from a test
|
|
1155
|
+
* mock (see test/fakeGenerator.ts).
|
|
1156
|
+
*/
|
|
1157
|
+
declare class PsBridgeHost implements PluginHost {
|
|
1158
|
+
readonly generator: PsGenerator;
|
|
1159
|
+
readonly config: PluginConfig;
|
|
1160
|
+
readonly logger: Logger;
|
|
1161
|
+
private server;
|
|
1162
|
+
/** Feature modules, reached by short key (`host.modules.layer`, ADR 0009). */
|
|
1163
|
+
readonly modules: {
|
|
1164
|
+
layer: LayerModule;
|
|
1165
|
+
document: DocumentModule;
|
|
1166
|
+
action: ActionModule;
|
|
1167
|
+
image: ImageModule;
|
|
1168
|
+
};
|
|
1169
|
+
private plugins;
|
|
1170
|
+
private readonly _jsx;
|
|
1171
|
+
private readonly _events;
|
|
1172
|
+
/**
|
|
1173
|
+
* Optional object-storage upload service (RFC 0008). Set from the environment
|
|
1174
|
+
* at construction — present only when the `PS_BRIDGE_COS_*` fields are configured;
|
|
1175
|
+
* otherwise undefined. Reached by modules and plugins through `plugin.cos`.
|
|
1176
|
+
*/
|
|
1177
|
+
readonly cos?: CosService;
|
|
1178
|
+
private constructor();
|
|
1179
|
+
/** Run packaged jsx by name (ADR 0008). Used by modules and other server callers. */
|
|
1180
|
+
get jsx(): JsxRunner;
|
|
1181
|
+
/** Photoshop event subscriptions owned by the host. */
|
|
1182
|
+
get events(): EventManager;
|
|
1183
|
+
/**
|
|
1184
|
+
* Build the host contract for one plugin (RFC 0005). A shallow view that shares
|
|
1185
|
+
* the host's `modules` and `events` (both global-singleton semantics — they do
|
|
1186
|
+
* not split per plugin) but swaps in a `jsx` scoped to `<pluginDir>/jsx`, so the
|
|
1187
|
+
* plugin's `jsx.execute("x")` resolves to its own files while `executeBuiltin`
|
|
1188
|
+
* still reaches the built-in tree. Passed to `loadPlugins` as the `hostFor`
|
|
1189
|
+
* factory; the plugin never sees the concrete `PsBridgeHost`.
|
|
1190
|
+
*/
|
|
1191
|
+
private hostFor;
|
|
1192
|
+
/** Entry point: construct the host and run its async initialization. */
|
|
1193
|
+
static init(generator: PsGenerator, config: PluginConfig, logger: Logger, overrides?: JsxRunnerOverrides): Promise<PsBridgeHost>;
|
|
1194
|
+
private onInit;
|
|
1195
|
+
private createMenuItem;
|
|
1196
|
+
private handleMenuClicked;
|
|
1197
|
+
/** Stop the WebSocket service (used by tests; PS teardown is process exit). */
|
|
1198
|
+
close(): Promise<void>;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/**
|
|
1202
|
+
* Base class for feature modules (ADR 0006). A module reaches every dependency
|
|
1203
|
+
* through `this.plugin` — `this.plugin.generator` for Photoshop, `this.plugin.logger`,
|
|
1204
|
+
* and `this.plugin.emit` / `this.plugin.broadcast` to push Events. Methods are
|
|
1205
|
+
* exposed via the `@ws` / `@api` decorators and wired up by `bootstrap`.
|
|
1206
|
+
*/
|
|
1207
|
+
declare abstract class BaseModule {
|
|
1208
|
+
readonly name: string;
|
|
1209
|
+
readonly plugin: PsBridgeHost;
|
|
1210
|
+
constructor(name: string, plugin: PsBridgeHost);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
declare class PsLayer {
|
|
1214
|
+
id: number;
|
|
1215
|
+
index: number;
|
|
1216
|
+
name: string;
|
|
1217
|
+
type: number;
|
|
1218
|
+
visible: boolean;
|
|
1219
|
+
bounds: PsBounds$1;
|
|
1220
|
+
rect: PsRect;
|
|
1221
|
+
clip: boolean;
|
|
1222
|
+
children?: PsLayer[];
|
|
1223
|
+
constructor(init: Partial<PsLayer>);
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* The Layer module surface a Plugin reaches through `plugin.modules.layer`
|
|
1227
|
+
* (RFC 0003). `LayerModule implements` this, so the plugin contract tracks the
|
|
1228
|
+
* implementation by compiler force; the SDK re-exports it via src/contract.ts.
|
|
1229
|
+
*/
|
|
1230
|
+
interface LayerModuleApi {
|
|
1231
|
+
getLayerInfo(options?: {
|
|
1232
|
+
id?: number;
|
|
1233
|
+
index?: number;
|
|
1234
|
+
getChildren?: boolean;
|
|
1235
|
+
getGeneratorSettings?: boolean;
|
|
1236
|
+
}): Promise<PsLayer>;
|
|
1237
|
+
getLayerInfoByID(layerID: number, options?: {
|
|
1238
|
+
getChildren: boolean;
|
|
1239
|
+
}): Promise<PsLayer>;
|
|
1240
|
+
getLayerInfoByIndex(layerIndex: number, options?: {
|
|
1241
|
+
getChildren: boolean;
|
|
1242
|
+
}): Promise<PsLayer>;
|
|
1243
|
+
}
|
|
1244
|
+
declare class LayerModule extends BaseModule implements LayerModuleApi {
|
|
1245
|
+
constructor(plugin: PsBridgeHost);
|
|
1246
|
+
getLayerInfo(options?: {
|
|
1247
|
+
id?: number;
|
|
1248
|
+
index?: number;
|
|
1249
|
+
getChildren?: boolean;
|
|
1250
|
+
getGeneratorSettings?: boolean;
|
|
1251
|
+
}): Promise<PsLayer>;
|
|
1252
|
+
getLayerInfoByID(layerIDOrParams: number | {
|
|
1253
|
+
layerID: number;
|
|
1254
|
+
options?: {
|
|
1255
|
+
getChildren: boolean;
|
|
1256
|
+
};
|
|
1257
|
+
}, options?: {
|
|
1258
|
+
getChildren: boolean;
|
|
1259
|
+
}): Promise<PsLayer>;
|
|
1260
|
+
getLayerInfoByIndex(layerIndexOrParams: number | {
|
|
1261
|
+
layerIndex: number;
|
|
1262
|
+
options?: {
|
|
1263
|
+
getChildren: boolean;
|
|
1264
|
+
};
|
|
1265
|
+
}, options?: {
|
|
1266
|
+
getChildren: boolean;
|
|
1267
|
+
}): Promise<PsLayer>;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
export { type ActionModuleApi as A, type Bounds as B, type CosServiceApi as C, type DocumentModuleApi as D, type ImageChangedEvent as I, JsxRunner as J, type LayerModuleApi as L, PsGenerator as P, type PluginConfig as a, PsBridgeHost as b, type ImageChangedLayer as c, type ImageModuleApi as d, type ImageResult as e, type JsxRunnerApi as f, type LayerSpec as g, type PhotoshopEventListener as h, type PhotoshopEventMap as i, type PhotoshopEvents as j, type PsBounds$1 as k, type PsDocument as l, PsLayer as m, type PsRect as n };
|