@alisaitteke/photoshop-mcp 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.
Files changed (115) hide show
  1. package/.prettierrc +9 -0
  2. package/README.md +1300 -0
  3. package/dist/api/batch-play.d.ts +62 -0
  4. package/dist/api/batch-play.d.ts.map +1 -0
  5. package/dist/api/batch-play.js +131 -0
  6. package/dist/api/batch-play.js.map +1 -0
  7. package/dist/api/extendscript.d.ts +258 -0
  8. package/dist/api/extendscript.d.ts.map +1 -0
  9. package/dist/api/extendscript.js +1437 -0
  10. package/dist/api/extendscript.js.map +1 -0
  11. package/dist/api/photoshop-api.d.ts +20 -0
  12. package/dist/api/photoshop-api.d.ts.map +1 -0
  13. package/dist/api/photoshop-api.js +88 -0
  14. package/dist/api/photoshop-api.js.map +1 -0
  15. package/dist/core/server.d.ts +14 -0
  16. package/dist/core/server.d.ts.map +1 -0
  17. package/dist/core/server.js +191 -0
  18. package/dist/core/server.js.map +1 -0
  19. package/dist/core/session.d.ts +24 -0
  20. package/dist/core/session.d.ts.map +1 -0
  21. package/dist/core/session.js +83 -0
  22. package/dist/core/session.js.map +1 -0
  23. package/dist/core/tool-registry.d.ts +23 -0
  24. package/dist/core/tool-registry.d.ts.map +1 -0
  25. package/dist/core/tool-registry.js +55 -0
  26. package/dist/core/tool-registry.js.map +1 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +18 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/platform/connection.d.ts +20 -0
  32. package/dist/platform/connection.d.ts.map +1 -0
  33. package/dist/platform/connection.js +94 -0
  34. package/dist/platform/connection.js.map +1 -0
  35. package/dist/platform/detector.d.ts +19 -0
  36. package/dist/platform/detector.d.ts.map +1 -0
  37. package/dist/platform/detector.js +63 -0
  38. package/dist/platform/detector.js.map +1 -0
  39. package/dist/platform/macos-detector.d.ts +13 -0
  40. package/dist/platform/macos-detector.d.ts.map +1 -0
  41. package/dist/platform/macos-detector.js +145 -0
  42. package/dist/platform/macos-detector.js.map +1 -0
  43. package/dist/platform/macos-executor.d.ts +22 -0
  44. package/dist/platform/macos-executor.d.ts.map +1 -0
  45. package/dist/platform/macos-executor.js +161 -0
  46. package/dist/platform/macos-executor.js.map +1 -0
  47. package/dist/platform/script-executor.d.ts +23 -0
  48. package/dist/platform/script-executor.d.ts.map +1 -0
  49. package/dist/platform/script-executor.js +2 -0
  50. package/dist/platform/script-executor.js.map +1 -0
  51. package/dist/platform/windows-detector.d.ts +14 -0
  52. package/dist/platform/windows-detector.d.ts.map +1 -0
  53. package/dist/platform/windows-detector.js +173 -0
  54. package/dist/platform/windows-detector.js.map +1 -0
  55. package/dist/platform/windows-executor.d.ts +15 -0
  56. package/dist/platform/windows-executor.d.ts.map +1 -0
  57. package/dist/platform/windows-executor.js +148 -0
  58. package/dist/platform/windows-executor.js.map +1 -0
  59. package/dist/tools/action-tools.d.ts +4 -0
  60. package/dist/tools/action-tools.d.ts.map +1 -0
  61. package/dist/tools/action-tools.js +102 -0
  62. package/dist/tools/action-tools.js.map +1 -0
  63. package/dist/tools/adjustment-tools.d.ts +4 -0
  64. package/dist/tools/adjustment-tools.d.ts.map +1 -0
  65. package/dist/tools/adjustment-tools.js +274 -0
  66. package/dist/tools/adjustment-tools.js.map +1 -0
  67. package/dist/tools/document-tools.d.ts +4 -0
  68. package/dist/tools/document-tools.d.ts.map +1 -0
  69. package/dist/tools/document-tools.js +230 -0
  70. package/dist/tools/document-tools.js.map +1 -0
  71. package/dist/tools/filter-tools.d.ts +4 -0
  72. package/dist/tools/filter-tools.d.ts.map +1 -0
  73. package/dist/tools/filter-tools.js +230 -0
  74. package/dist/tools/filter-tools.js.map +1 -0
  75. package/dist/tools/history-tools.d.ts +4 -0
  76. package/dist/tools/history-tools.d.ts.map +1 -0
  77. package/dist/tools/history-tools.js +137 -0
  78. package/dist/tools/history-tools.js.map +1 -0
  79. package/dist/tools/image-placement-tools.d.ts +4 -0
  80. package/dist/tools/image-placement-tools.d.ts.map +1 -0
  81. package/dist/tools/image-placement-tools.js +109 -0
  82. package/dist/tools/image-placement-tools.js.map +1 -0
  83. package/dist/tools/image-tools.d.ts +4 -0
  84. package/dist/tools/image-tools.d.ts.map +1 -0
  85. package/dist/tools/image-tools.js +123 -0
  86. package/dist/tools/image-tools.js.map +1 -0
  87. package/dist/tools/layer-ordering-tools.d.ts +4 -0
  88. package/dist/tools/layer-ordering-tools.d.ts.map +1 -0
  89. package/dist/tools/layer-ordering-tools.js +210 -0
  90. package/dist/tools/layer-ordering-tools.js.map +1 -0
  91. package/dist/tools/layer-properties-tools.d.ts +4 -0
  92. package/dist/tools/layer-properties-tools.d.ts.map +1 -0
  93. package/dist/tools/layer-properties-tools.js +421 -0
  94. package/dist/tools/layer-properties-tools.js.map +1 -0
  95. package/dist/tools/layer-tools.d.ts +4 -0
  96. package/dist/tools/layer-tools.d.ts.map +1 -0
  97. package/dist/tools/layer-tools.js +251 -0
  98. package/dist/tools/layer-tools.js.map +1 -0
  99. package/dist/tools/layer-transform-tools.d.ts +4 -0
  100. package/dist/tools/layer-transform-tools.d.ts.map +1 -0
  101. package/dist/tools/layer-transform-tools.js +199 -0
  102. package/dist/tools/layer-transform-tools.js.map +1 -0
  103. package/dist/tools/selection-tools.d.ts +4 -0
  104. package/dist/tools/selection-tools.d.ts.map +1 -0
  105. package/dist/tools/selection-tools.js +295 -0
  106. package/dist/tools/selection-tools.js.map +1 -0
  107. package/dist/tools/text-tools.d.ts +4 -0
  108. package/dist/tools/text-tools.d.ts.map +1 -0
  109. package/dist/tools/text-tools.js +210 -0
  110. package/dist/tools/text-tools.js.map +1 -0
  111. package/dist/utils/logger.d.ts +17 -0
  112. package/dist/utils/logger.d.ts.map +1 -0
  113. package/dist/utils/logger.js +43 -0
  114. package/dist/utils/logger.js.map +1 -0
  115. package/package.json +47 -0
@@ -0,0 +1,1437 @@
1
+ /**
2
+ * Helper functions for ExtendScript API
3
+ * ExtendScript is the legacy scripting API for Photoshop
4
+ */
5
+ /**
6
+ * Helper functions for character/string ID conversion
7
+ */
8
+ const helperFunctions = `
9
+ function cTID(s) { return app.charIDToTypeID(s); }
10
+ function sTID(s) { return app.stringIDToTypeID(s); }
11
+ `;
12
+ /**
13
+ * Helper function to get current context information
14
+ */
15
+ const getContextInfo = `
16
+ function getContextInfo() {
17
+ var context = {
18
+ hasDocument: app.documents.length > 0
19
+ };
20
+
21
+ if (context.hasDocument) {
22
+ var doc = app.activeDocument;
23
+ context.document = {
24
+ name: doc.name,
25
+ width: doc.width.as('px'),
26
+ height: doc.height.as('px'),
27
+ resolution: doc.resolution,
28
+ colorMode: String(doc.mode),
29
+ layerCount: doc.layers.length,
30
+ hasSelection: doc.selection.bounds ? true : false
31
+ };
32
+
33
+ if (doc.activeLayer) {
34
+ var layer = doc.activeLayer;
35
+ context.activeLayer = {
36
+ name: layer.name,
37
+ kind: String(layer.kind),
38
+ opacity: layer.opacity,
39
+ blendMode: String(layer.blendMode),
40
+ visible: layer.visible,
41
+ locked: layer.allLocked,
42
+ isBackground: layer.isBackgroundLayer
43
+ };
44
+
45
+ // Add bounds if available
46
+ try {
47
+ var bounds = layer.bounds;
48
+ context.activeLayer.bounds = {
49
+ left: bounds[0].as('px'),
50
+ top: bounds[1].as('px'),
51
+ right: bounds[2].as('px'),
52
+ bottom: bounds[3].as('px')
53
+ };
54
+ } catch (e) {
55
+ // Bounds not available for some layer types
56
+ }
57
+ }
58
+ }
59
+
60
+ return context;
61
+ }
62
+ `;
63
+ /**
64
+ * Common ExtendScript snippets
65
+ */
66
+ export const ExtendScriptSnippets = {
67
+ /**
68
+ * Get Photoshop application info
69
+ */
70
+ getAppInfo: () => `
71
+ return {
72
+ name: app.name,
73
+ version: app.version,
74
+ build: app.build
75
+ };
76
+ `,
77
+ /**
78
+ * Create a new document
79
+ */
80
+ newDocument: (width, height, resolution = 72, colorMode = 'NewDocumentMode.RGB') => `
81
+ var doc = app.documents.add(
82
+ UnitValue(${width}, 'px'),
83
+ UnitValue(${height}, 'px'),
84
+ ${resolution},
85
+ 'New Document',
86
+ ${colorMode}
87
+ );
88
+ return { id: doc.id, name: doc.name };
89
+ `,
90
+ /**
91
+ * Get active document info
92
+ */
93
+ getDocumentInfo: () => `
94
+ ${getContextInfo}
95
+
96
+ if (app.documents.length === 0) {
97
+ throw new Error('No active document');
98
+ }
99
+
100
+ var context = getContextInfo();
101
+ return context;
102
+ `,
103
+ /**
104
+ * Create a text layer
105
+ */
106
+ createTextLayer: (text, x = 100, y = 100, fontSize = 24) => `
107
+ ${getContextInfo}
108
+
109
+ if (app.documents.length === 0) {
110
+ throw new Error('No active document');
111
+ }
112
+ var doc = app.activeDocument;
113
+ var textLayer = doc.artLayers.add();
114
+ textLayer.kind = LayerKind.TEXT;
115
+ textLayer.textItem.contents = "${text.replace(/"/g, '\\"')}";
116
+ textLayer.textItem.position = [${x}, ${y}];
117
+ textLayer.textItem.size = ${fontSize};
118
+
119
+ var result = {
120
+ created: true,
121
+ layerName: textLayer.name,
122
+ text: "${text.replace(/"/g, '\\"')}",
123
+ position: { x: ${x}, y: ${y} },
124
+ fontSize: ${fontSize},
125
+ context: getContextInfo()
126
+ };
127
+ return result;
128
+ `,
129
+ /**
130
+ * Place an image file as a layer
131
+ */
132
+ placeImage: (filePath, x = 0, y = 0) => `
133
+ ${helperFunctions}
134
+ ${getContextInfo}
135
+
136
+ if (app.documents.length === 0) {
137
+ throw new Error('No active document');
138
+ }
139
+
140
+ var imageFile = new File("${filePath.replace(/\\/g, '\\\\')}");
141
+ if (!imageFile.exists) {
142
+ throw new Error('Image file not found: ${filePath}');
143
+ }
144
+
145
+ // Place image using ActionDescriptor
146
+ var desc = new ActionDescriptor();
147
+ desc.putPath(cTID('null'), imageFile);
148
+ desc.putEnumerated(cTID('FTcs'), cTID('QCSt'), cTID('Qcsa'));
149
+
150
+ var offsetDesc = new ActionDescriptor();
151
+ offsetDesc.putUnitDouble(cTID('Hrzn'), cTID('#Pxl'), ${x});
152
+ offsetDesc.putUnitDouble(cTID('Vrtc'), cTID('#Pxl'), ${y});
153
+ desc.putObject(cTID('Ofst'), cTID('Ofst'), offsetDesc);
154
+
155
+ executeAction(cTID('Plc '), desc, DialogModes.NO);
156
+
157
+ var layer = app.activeDocument.activeLayer;
158
+ var result = {
159
+ placed: true,
160
+ layerName: layer.name,
161
+ filePath: "${filePath}",
162
+ position: { x: ${x}, y: ${y} },
163
+ layerBounds: {
164
+ width: layer.bounds[2].as('px') - layer.bounds[0].as('px'),
165
+ height: layer.bounds[3].as('px') - layer.bounds[1].as('px')
166
+ },
167
+ context: getContextInfo()
168
+ };
169
+ return result;
170
+ `,
171
+ /**
172
+ * Open an image file as a new document
173
+ */
174
+ openImage: (filePath) => `
175
+ var imageFile = new File("${filePath.replace(/\\/g, '\\\\')}");
176
+ if (!imageFile.exists) {
177
+ throw new Error('Image file not found: ${filePath}');
178
+ }
179
+
180
+ var doc = app.open(imageFile);
181
+ return {
182
+ id: doc.id,
183
+ name: doc.name,
184
+ width: doc.width.as('px'),
185
+ height: doc.height.as('px')
186
+ };
187
+ `,
188
+ /**
189
+ * Save document as PSD
190
+ */
191
+ saveAsPSD: (path) => `
192
+ if (app.documents.length === 0) {
193
+ throw new Error('No active document');
194
+ }
195
+ var doc = app.activeDocument;
196
+ var saveFile = new File("${path.replace(/\\/g, '\\\\')}");
197
+ var psdOptions = new PhotoshopSaveOptions();
198
+ psdOptions.embedColorProfile = true;
199
+ doc.saveAs(saveFile, psdOptions, true);
200
+ return { path: saveFile.fsName };
201
+ `,
202
+ /**
203
+ * Save document as JPEG
204
+ */
205
+ saveAsJPEG: (path, quality = 8) => `
206
+ if (app.documents.length === 0) {
207
+ throw new Error('No active document');
208
+ }
209
+ var doc = app.activeDocument;
210
+ var saveFile = new File("${path.replace(/\\/g, '\\\\')}");
211
+ var jpegOptions = new JPEGSaveOptions();
212
+ jpegOptions.quality = ${quality};
213
+ jpegOptions.embedColorProfile = true;
214
+ doc.saveAs(saveFile, jpegOptions, true);
215
+ return { path: saveFile.fsName };
216
+ `,
217
+ /**
218
+ * Save document as PNG
219
+ */
220
+ saveAsPNG: (path) => `
221
+ if (app.documents.length === 0) {
222
+ throw new Error('No active document');
223
+ }
224
+ var doc = app.activeDocument;
225
+ var saveFile = new File("${path.replace(/\\/g, '\\\\')}");
226
+ var pngOptions = new PNGSaveOptions();
227
+ pngOptions.compression = 9;
228
+ doc.saveAs(saveFile, pngOptions, true);
229
+ return { path: saveFile.fsName };
230
+ `,
231
+ /**
232
+ * Close active document
233
+ */
234
+ closeDocument: (save = false) => `
235
+ if (app.documents.length === 0) {
236
+ throw new Error('No active document');
237
+ }
238
+ var doc = app.activeDocument;
239
+ doc.close(${save ? 'SaveOptions.SAVECHANGES' : 'SaveOptions.DONOTSAVECHANGES'});
240
+ return { closed: true };
241
+ `,
242
+ /**
243
+ * Create a new layer
244
+ */
245
+ newLayer: (name) => `
246
+ ${getContextInfo}
247
+
248
+ if (app.documents.length === 0) {
249
+ throw new Error('No active document');
250
+ }
251
+ var doc = app.activeDocument;
252
+ var layer = doc.artLayers.add();
253
+ ${name ? `layer.name = "${name.replace(/"/g, '\\"')}";` : ''}
254
+
255
+ var result = {
256
+ created: true,
257
+ layerName: layer.name,
258
+ context: getContextInfo()
259
+ };
260
+ return result;
261
+ `,
262
+ /**
263
+ * Delete active layer
264
+ */
265
+ deleteLayer: () => `
266
+ if (app.documents.length === 0) {
267
+ throw new Error('No active document');
268
+ }
269
+ var doc = app.activeDocument;
270
+ if (doc.activeLayer) {
271
+ doc.activeLayer.remove();
272
+ return { deleted: true };
273
+ }
274
+ throw new Error('No active layer');
275
+ `,
276
+ /**
277
+ * Fill layer with color
278
+ */
279
+ fillLayer: (red, green, blue) => `
280
+ if (app.documents.length === 0) {
281
+ throw new Error('No active document');
282
+ }
283
+ var doc = app.activeDocument;
284
+ var color = new SolidColor();
285
+ color.rgb.red = ${red};
286
+ color.rgb.green = ${green};
287
+ color.rgb.blue = ${blue};
288
+ doc.activeLayer.fillPath(color);
289
+ return { filled: true };
290
+ `,
291
+ /**
292
+ * Resize image
293
+ */
294
+ resizeImage: (width, height) => `
295
+ if (app.documents.length === 0) {
296
+ throw new Error('No active document');
297
+ }
298
+ var doc = app.activeDocument;
299
+ doc.resizeImage(
300
+ UnitValue(${width}, 'px'),
301
+ UnitValue(${height}, 'px'),
302
+ null,
303
+ ResampleMethod.BICUBIC
304
+ );
305
+ return {
306
+ width: doc.width.as('px'),
307
+ height: doc.height.as('px')
308
+ };
309
+ `,
310
+ /**
311
+ * Get all layer names
312
+ */
313
+ getLayerNames: () => `
314
+ ${getContextInfo}
315
+
316
+ if (app.documents.length === 0) {
317
+ throw new Error('No active document');
318
+ }
319
+ var doc = app.activeDocument;
320
+ var layers = [];
321
+ for (var i = 0; i < doc.layers.length; i++) {
322
+ var layer = doc.layers[i];
323
+ layers.push({
324
+ name: layer.name,
325
+ kind: String(layer.kind),
326
+ visible: layer.visible,
327
+ opacity: layer.opacity,
328
+ blendMode: String(layer.blendMode)
329
+ });
330
+ }
331
+
332
+ var result = {
333
+ layerCount: layers.length,
334
+ layers: layers,
335
+ context: getContextInfo()
336
+ };
337
+ return result;
338
+ `,
339
+ /**
340
+ * Select layer by name
341
+ */
342
+ selectLayer: (name) => `
343
+ if (app.documents.length === 0) {
344
+ throw new Error('No active document');
345
+ }
346
+ var doc = app.activeDocument;
347
+ for (var i = 0; i < doc.layers.length; i++) {
348
+ if (doc.layers[i].name === "${name.replace(/"/g, '\\"')}") {
349
+ doc.activeLayer = doc.layers[i];
350
+ return { selected: true, name: doc.layers[i].name };
351
+ }
352
+ }
353
+ throw new Error('Layer not found: ${name.replace(/"/g, '\\"')}');
354
+ `,
355
+ /**
356
+ * Scale active layer to fit document (maintain aspect ratio)
357
+ */
358
+ fitLayerToDocument: (fillDocument = false) => `
359
+ ${getContextInfo}
360
+
361
+ if (app.documents.length === 0) {
362
+ throw new Error('No active document');
363
+ }
364
+ var doc = app.activeDocument;
365
+ var layer = doc.activeLayer;
366
+
367
+ if (layer.isBackgroundLayer) {
368
+ throw new Error('Cannot transform background layer');
369
+ }
370
+
371
+ // Get canvas dimensions
372
+ var canvasWidth = doc.width.as('px');
373
+ var canvasHeight = doc.height.as('px');
374
+
375
+ // Get layer bounds
376
+ var bounds = layer.bounds;
377
+ var layerWidth = bounds[2].as('px') - bounds[0].as('px');
378
+ var layerHeight = bounds[3].as('px') - bounds[1].as('px');
379
+
380
+ // Calculate scale ratios
381
+ var widthRatio = canvasWidth / layerWidth;
382
+ var heightRatio = canvasHeight / layerHeight;
383
+
384
+ // Choose scale factor based on fill or fit mode
385
+ var scaleFactor;
386
+ if (${fillDocument}) {
387
+ // Fill: scale to cover entire canvas (may crop)
388
+ scaleFactor = Math.max(widthRatio, heightRatio);
389
+ } else {
390
+ // Fit: scale to fit within canvas (may have margins)
391
+ scaleFactor = Math.min(widthRatio, heightRatio);
392
+ }
393
+
394
+ // Apply scale
395
+ var scalePercent = scaleFactor * 100;
396
+ layer.resize(scalePercent, scalePercent, AnchorPosition.MIDDLECENTER);
397
+
398
+ // Center the layer
399
+ layer.translate(
400
+ canvasWidth / 2 - (bounds[0].as('px') + layerWidth / 2),
401
+ canvasHeight / 2 - (bounds[1].as('px') + layerHeight / 2)
402
+ );
403
+
404
+ var result = {
405
+ fitted: true,
406
+ mode: ${fillDocument} ? 'fill' : 'fit',
407
+ originalSize: { width: layerWidth, height: layerHeight },
408
+ newSize: {
409
+ width: layerWidth * scaleFactor,
410
+ height: layerHeight * scaleFactor
411
+ },
412
+ scaleFactor: scaleFactor,
413
+ scalePercent: scalePercent,
414
+ context: getContextInfo()
415
+ };
416
+ return result;
417
+ `,
418
+ /**
419
+ * Scale active layer by percentage
420
+ */
421
+ scaleLayer: (scalePercent, centerAnchor = true) => `
422
+ if (app.documents.length === 0) {
423
+ throw new Error('No active document');
424
+ }
425
+ var doc = app.activeDocument;
426
+ var layer = doc.activeLayer;
427
+
428
+ if (layer.isBackgroundLayer) {
429
+ throw new Error('Cannot transform background layer');
430
+ }
431
+
432
+ var anchor = ${centerAnchor ? 'AnchorPosition.MIDDLECENTER' : 'AnchorPosition.TOPLEFT'};
433
+ layer.resize(${scalePercent}, ${scalePercent}, anchor);
434
+
435
+ return {
436
+ scaled: true,
437
+ percent: ${scalePercent}
438
+ };
439
+ `,
440
+ /**
441
+ * Move/translate active layer
442
+ */
443
+ moveLayer: (deltaX, deltaY) => `
444
+ if (app.documents.length === 0) {
445
+ throw new Error('No active document');
446
+ }
447
+ var doc = app.activeDocument;
448
+ var layer = doc.activeLayer;
449
+
450
+ if (layer.isBackgroundLayer) {
451
+ throw new Error('Cannot move background layer');
452
+ }
453
+
454
+ layer.translate(${deltaX}, ${deltaY});
455
+
456
+ return {
457
+ moved: true,
458
+ deltaX: ${deltaX},
459
+ deltaY: ${deltaY}
460
+ };
461
+ `,
462
+ /**
463
+ * Rotate active layer
464
+ */
465
+ rotateLayer: (degrees) => `
466
+ if (app.documents.length === 0) {
467
+ throw new Error('No active document');
468
+ }
469
+ var doc = app.activeDocument;
470
+ var layer = doc.activeLayer;
471
+
472
+ if (layer.isBackgroundLayer) {
473
+ throw new Error('Cannot rotate background layer');
474
+ }
475
+
476
+ layer.rotate(${degrees}, AnchorPosition.MIDDLECENTER);
477
+
478
+ return {
479
+ rotated: true,
480
+ degrees: ${degrees}
481
+ };
482
+ `,
483
+ /**
484
+ * Set layer opacity
485
+ */
486
+ setLayerOpacity: (opacity) => `
487
+ ${getContextInfo}
488
+
489
+ if (app.documents.length === 0) {
490
+ throw new Error('No active document');
491
+ }
492
+ var doc = app.activeDocument;
493
+ var layer = doc.activeLayer;
494
+
495
+ layer.opacity = ${opacity};
496
+
497
+ var result = {
498
+ updated: true,
499
+ property: 'opacity',
500
+ value: layer.opacity,
501
+ layerName: layer.name,
502
+ context: getContextInfo()
503
+ };
504
+ return result;
505
+ `,
506
+ /**
507
+ * Set layer blend mode
508
+ */
509
+ setLayerBlendMode: (blendMode) => `
510
+ ${getContextInfo}
511
+
512
+ if (app.documents.length === 0) {
513
+ throw new Error('No active document');
514
+ }
515
+ var doc = app.activeDocument;
516
+ var layer = doc.activeLayer;
517
+
518
+ layer.blendMode = BlendMode.${blendMode};
519
+
520
+ var result = {
521
+ updated: true,
522
+ property: 'blendMode',
523
+ value: String(layer.blendMode),
524
+ layerName: layer.name,
525
+ context: getContextInfo()
526
+ };
527
+ return result;
528
+ `,
529
+ /**
530
+ * Set layer visibility
531
+ */
532
+ setLayerVisibility: (visible) => `
533
+ if (app.documents.length === 0) {
534
+ throw new Error('No active document');
535
+ }
536
+ var doc = app.activeDocument;
537
+ var layer = doc.activeLayer;
538
+
539
+ layer.visible = ${visible};
540
+
541
+ return {
542
+ visible: layer.visible,
543
+ name: layer.name
544
+ };
545
+ `,
546
+ /**
547
+ * Lock/unlock layer
548
+ */
549
+ setLayerLocked: (locked) => `
550
+ if (app.documents.length === 0) {
551
+ throw new Error('No active document');
552
+ }
553
+ var doc = app.activeDocument;
554
+ var layer = doc.activeLayer;
555
+
556
+ layer.allLocked = ${locked};
557
+
558
+ return {
559
+ locked: layer.allLocked,
560
+ name: layer.name
561
+ };
562
+ `,
563
+ /**
564
+ * Rename active layer
565
+ */
566
+ renameLayer: (newName) => `
567
+ if (app.documents.length === 0) {
568
+ throw new Error('No active document');
569
+ }
570
+ var doc = app.activeDocument;
571
+ var layer = doc.activeLayer;
572
+
573
+ var oldName = layer.name;
574
+ layer.name = "${newName.replace(/"/g, '\\"')}";
575
+
576
+ return {
577
+ oldName: oldName,
578
+ newName: layer.name
579
+ };
580
+ `,
581
+ /**
582
+ * Duplicate active layer
583
+ */
584
+ duplicateLayer: (newName) => `
585
+ if (app.documents.length === 0) {
586
+ throw new Error('No active document');
587
+ }
588
+ var doc = app.activeDocument;
589
+ var layer = doc.activeLayer;
590
+
591
+ var duplicated = layer.duplicate();
592
+ ${newName ? `duplicated.name = "${newName.replace(/"/g, '\\"')}";` : ''}
593
+
594
+ return {
595
+ originalName: layer.name,
596
+ newName: duplicated.name
597
+ };
598
+ `,
599
+ /**
600
+ * Merge visible layers
601
+ */
602
+ mergeVisibleLayers: () => `
603
+ if (app.documents.length === 0) {
604
+ throw new Error('No active document');
605
+ }
606
+ var doc = app.activeDocument;
607
+ doc.mergeVisibleLayers();
608
+
609
+ return {
610
+ merged: true
611
+ };
612
+ `,
613
+ /**
614
+ * Flatten image (merge all layers)
615
+ */
616
+ flattenImage: () => `
617
+ if (app.documents.length === 0) {
618
+ throw new Error('No active document');
619
+ }
620
+ var doc = app.activeDocument;
621
+ doc.flatten();
622
+
623
+ return {
624
+ flattened: true
625
+ };
626
+ `,
627
+ /**
628
+ * Apply Gaussian Blur filter
629
+ */
630
+ applyGaussianBlur: (radius) => `
631
+ ${getContextInfo}
632
+
633
+ if (app.documents.length === 0) {
634
+ throw new Error('No active document');
635
+ }
636
+ var layer = app.activeDocument.activeLayer;
637
+ var wasRasterized = false;
638
+
639
+ // Auto-rasterize if needed
640
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
641
+ layer.rasterize(RasterizeType.ENTIRELAYER);
642
+ wasRasterized = true;
643
+ }
644
+
645
+ if (layer.kind !== LayerKind.NORMAL) {
646
+ throw new Error('Can only apply filters to normal (raster) layers. Layer kind: ' + layer.kind);
647
+ }
648
+
649
+ layer.applyGaussianBlur(${radius});
650
+
651
+ var result = {
652
+ applied: true,
653
+ filter: 'Gaussian Blur',
654
+ radius: ${radius},
655
+ wasRasterized: wasRasterized,
656
+ context: getContextInfo()
657
+ };
658
+ return result;
659
+ `,
660
+ /**
661
+ * Apply Unsharp Mask (sharpen)
662
+ */
663
+ applyUnsharpMask: (amount, radius, threshold) => `
664
+ if (app.documents.length === 0) {
665
+ throw new Error('No active document');
666
+ }
667
+ var layer = app.activeDocument.activeLayer;
668
+
669
+ // Auto-rasterize if needed
670
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
671
+ layer.rasterize(RasterizeType.ENTIRELAYER);
672
+ }
673
+
674
+ if (layer.kind !== LayerKind.NORMAL) {
675
+ throw new Error('Can only apply filters to normal (raster) layers');
676
+ }
677
+
678
+ layer.applyUnSharpMask(${amount}, ${radius}, ${threshold});
679
+
680
+ return {
681
+ filter: 'Unsharp Mask',
682
+ amount: ${amount},
683
+ radius: ${radius},
684
+ threshold: ${threshold}
685
+ };
686
+ `,
687
+ /**
688
+ * Apply Add Noise filter
689
+ */
690
+ applyAddNoise: (amount, distribution, monochromatic) => `
691
+ if (app.documents.length === 0) {
692
+ throw new Error('No active document');
693
+ }
694
+ var layer = app.activeDocument.activeLayer;
695
+
696
+ // Auto-rasterize if needed
697
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
698
+ layer.rasterize(RasterizeType.ENTIRELAYER);
699
+ }
700
+
701
+ if (layer.kind !== LayerKind.NORMAL) {
702
+ throw new Error('Can only apply filters to normal (raster) layers');
703
+ }
704
+
705
+ var distEnum = NoiseDistribution.${distribution};
706
+ layer.applyAddNoise(${amount}, distEnum, ${monochromatic});
707
+
708
+ return {
709
+ filter: 'Add Noise',
710
+ amount: ${amount},
711
+ distribution: '${distribution}',
712
+ monochromatic: ${monochromatic}
713
+ };
714
+ `,
715
+ /**
716
+ * Apply Motion Blur filter
717
+ */
718
+ applyMotionBlur: (angle, radius) => `
719
+ if (app.documents.length === 0) {
720
+ throw new Error('No active document');
721
+ }
722
+ var layer = app.activeDocument.activeLayer;
723
+
724
+ // Auto-rasterize if needed
725
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
726
+ layer.rasterize(RasterizeType.ENTIRELAYER);
727
+ }
728
+
729
+ if (layer.kind !== LayerKind.NORMAL) {
730
+ throw new Error('Can only apply filters to normal (raster) layers');
731
+ }
732
+
733
+ layer.applyMotionBlur(${angle}, ${radius});
734
+
735
+ return {
736
+ filter: 'Motion Blur',
737
+ angle: ${angle},
738
+ radius: ${radius}
739
+ };
740
+ `,
741
+ /**
742
+ * Adjust brightness and contrast
743
+ */
744
+ adjustBrightnessContrast: (brightness, contrast) => `
745
+ if (app.documents.length === 0) {
746
+ throw new Error('No active document');
747
+ }
748
+ var layer = app.activeDocument.activeLayer;
749
+
750
+ // Auto-rasterize if needed
751
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
752
+ layer.rasterize(RasterizeType.ENTIRELAYER);
753
+ }
754
+
755
+ layer.adjustBrightnessContrast(${brightness}, ${contrast});
756
+
757
+ return {
758
+ adjustment: 'Brightness/Contrast',
759
+ brightness: ${brightness},
760
+ contrast: ${contrast}
761
+ };
762
+ `,
763
+ /**
764
+ * Adjust hue and saturation
765
+ */
766
+ adjustHueSaturation: (hue, saturation, lightness) => `
767
+ if (app.documents.length === 0) {
768
+ throw new Error('No active document');
769
+ }
770
+ var layer = app.activeDocument.activeLayer;
771
+
772
+ layer.adjustColorBalance([${hue}], [${saturation}], [${lightness}]);
773
+
774
+ return {
775
+ adjustment: 'Hue/Saturation',
776
+ hue: ${hue},
777
+ saturation: ${saturation},
778
+ lightness: ${lightness}
779
+ };
780
+ `,
781
+ /**
782
+ * Auto levels adjustment
783
+ */
784
+ autoLevels: () => `
785
+ if (app.documents.length === 0) {
786
+ throw new Error('No active document');
787
+ }
788
+ var layer = app.activeDocument.activeLayer;
789
+
790
+ // Auto-rasterize if needed
791
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
792
+ layer.rasterize(RasterizeType.ENTIRELAYER);
793
+ }
794
+
795
+ layer.autoLevels();
796
+
797
+ return {
798
+ adjustment: 'Auto Levels'
799
+ };
800
+ `,
801
+ /**
802
+ * Auto contrast adjustment
803
+ */
804
+ autoContrast: () => `
805
+ if (app.documents.length === 0) {
806
+ throw new Error('No active document');
807
+ }
808
+ var layer = app.activeDocument.activeLayer;
809
+
810
+ // Auto-rasterize if needed
811
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
812
+ layer.rasterize(RasterizeType.ENTIRELAYER);
813
+ }
814
+
815
+ layer.autoContrast();
816
+
817
+ return {
818
+ adjustment: 'Auto Contrast'
819
+ };
820
+ `,
821
+ /**
822
+ * Desaturate (convert to grayscale without changing color mode)
823
+ */
824
+ desaturate: () => `
825
+ if (app.documents.length === 0) {
826
+ throw new Error('No active document');
827
+ }
828
+ var layer = app.activeDocument.activeLayer;
829
+
830
+ // Auto-rasterize if needed
831
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
832
+ layer.rasterize(RasterizeType.ENTIRELAYER);
833
+ }
834
+
835
+ layer.desaturate();
836
+
837
+ return {
838
+ adjustment: 'Desaturate'
839
+ };
840
+ `,
841
+ /**
842
+ * Invert colors
843
+ */
844
+ invert: () => `
845
+ if (app.documents.length === 0) {
846
+ throw new Error('No active document');
847
+ }
848
+ var layer = app.activeDocument.activeLayer;
849
+
850
+ // Auto-rasterize if needed
851
+ if (layer.kind === LayerKind.TEXT || layer.kind === LayerKind.SMARTOBJECT) {
852
+ layer.rasterize(RasterizeType.ENTIRELAYER);
853
+ }
854
+
855
+ layer.invert();
856
+
857
+ return {
858
+ adjustment: 'Invert'
859
+ };
860
+ `,
861
+ /**
862
+ * Crop document
863
+ */
864
+ cropDocument: (left, top, right, bottom) => `
865
+ if (app.documents.length === 0) {
866
+ throw new Error('No active document');
867
+ }
868
+ var doc = app.activeDocument;
869
+
870
+ var bounds = [${left}, ${top}, ${right}, ${bottom}];
871
+ doc.crop(bounds);
872
+
873
+ return {
874
+ cropped: true,
875
+ newWidth: doc.width.as('px'),
876
+ newHeight: doc.height.as('px')
877
+ };
878
+ `,
879
+ /**
880
+ * Set text layer font
881
+ */
882
+ setTextFont: (fontName, fontSize) => `
883
+ if (app.documents.length === 0) {
884
+ throw new Error('No active document');
885
+ }
886
+ var layer = app.activeDocument.activeLayer;
887
+
888
+ if (layer.kind !== LayerKind.TEXT) {
889
+ throw new Error('Active layer is not a text layer');
890
+ }
891
+
892
+ layer.textItem.font = "${fontName.replace(/"/g, '\\"')}";
893
+ ${fontSize ? `layer.textItem.size = ${fontSize};` : ''}
894
+
895
+ return {
896
+ font: layer.textItem.font,
897
+ size: layer.textItem.size
898
+ };
899
+ `,
900
+ /**
901
+ * Set text color
902
+ */
903
+ setTextColor: (red, green, blue) => `
904
+ if (app.documents.length === 0) {
905
+ throw new Error('No active document');
906
+ }
907
+ var layer = app.activeDocument.activeLayer;
908
+
909
+ if (layer.kind !== LayerKind.TEXT) {
910
+ throw new Error('Active layer is not a text layer');
911
+ }
912
+
913
+ var color = new SolidColor();
914
+ color.rgb.red = ${red};
915
+ color.rgb.green = ${green};
916
+ color.rgb.blue = ${blue};
917
+ layer.textItem.color = color;
918
+
919
+ return {
920
+ color: 'RGB(' + ${red} + ', ' + ${green} + ', ' + ${blue} + ')'
921
+ };
922
+ `,
923
+ /**
924
+ * Set text alignment
925
+ */
926
+ setTextAlignment: (alignment) => `
927
+ if (app.documents.length === 0) {
928
+ throw new Error('No active document');
929
+ }
930
+ var layer = app.activeDocument.activeLayer;
931
+
932
+ if (layer.kind !== LayerKind.TEXT) {
933
+ throw new Error('Active layer is not a text layer');
934
+ }
935
+
936
+ layer.textItem.justification = Justification.${alignment};
937
+
938
+ return {
939
+ alignment: '${alignment}'
940
+ };
941
+ `,
942
+ /**
943
+ * Update text content
944
+ */
945
+ updateTextContent: (newText) => `
946
+ if (app.documents.length === 0) {
947
+ throw new Error('No active document');
948
+ }
949
+ var layer = app.activeDocument.activeLayer;
950
+
951
+ if (layer.kind !== LayerKind.TEXT) {
952
+ throw new Error('Active layer is not a text layer');
953
+ }
954
+
955
+ layer.textItem.contents = "${newText.replace(/"/g, '\\"')}";
956
+
957
+ return {
958
+ text: layer.textItem.contents
959
+ };
960
+ `,
961
+ /**
962
+ * Create rectangular selection
963
+ */
964
+ selectRectangle: (left, top, right, bottom) => `
965
+ if (app.documents.length === 0) {
966
+ throw new Error('No active document');
967
+ }
968
+ var doc = app.activeDocument;
969
+
970
+ var bounds = [[${left}, ${top}], [${right}, ${top}], [${right}, ${bottom}], [${left}, ${bottom}]];
971
+ doc.selection.select(bounds);
972
+
973
+ return {
974
+ selection: 'rectangle',
975
+ bounds: [${left}, ${top}, ${right}, ${bottom}]
976
+ };
977
+ `,
978
+ /**
979
+ * Select all
980
+ */
981
+ selectAll: () => `
982
+ if (app.documents.length === 0) {
983
+ throw new Error('No active document');
984
+ }
985
+ var doc = app.activeDocument;
986
+ doc.selection.selectAll();
987
+
988
+ return {
989
+ selection: 'all'
990
+ };
991
+ `,
992
+ /**
993
+ * Deselect
994
+ */
995
+ deselect: () => `
996
+ if (app.documents.length === 0) {
997
+ throw new Error('No active document');
998
+ }
999
+ var doc = app.activeDocument;
1000
+ doc.selection.deselect();
1001
+
1002
+ return {
1003
+ deselected: true
1004
+ };
1005
+ `,
1006
+ /**
1007
+ * Invert selection
1008
+ */
1009
+ invertSelection: () => `
1010
+ if (app.documents.length === 0) {
1011
+ throw new Error('No active document');
1012
+ }
1013
+ var doc = app.activeDocument;
1014
+ doc.selection.invert();
1015
+
1016
+ return {
1017
+ inverted: true
1018
+ };
1019
+ `,
1020
+ /**
1021
+ * Create layer mask from selection
1022
+ */
1023
+ createLayerMask: () => `
1024
+ ${helperFunctions}
1025
+
1026
+ if (app.documents.length === 0) {
1027
+ throw new Error('No active document');
1028
+ }
1029
+
1030
+ // Create mask using ActionDescriptor
1031
+ var desc = new ActionDescriptor();
1032
+ var ref = new ActionReference();
1033
+ ref.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Msk '));
1034
+ desc.putReference(cTID('Nw '), ref);
1035
+ desc.putEnumerated(cTID('Usng'), cTID('UsrM'), cTID('RvlS'));
1036
+ executeAction(cTID('Mk '), desc, DialogModes.NO);
1037
+
1038
+ return {
1039
+ maskCreated: true
1040
+ };
1041
+ `,
1042
+ /**
1043
+ * Delete layer mask
1044
+ */
1045
+ deleteLayerMask: () => `
1046
+ ${helperFunctions}
1047
+
1048
+ if (app.documents.length === 0) {
1049
+ throw new Error('No active document');
1050
+ }
1051
+
1052
+ var desc = new ActionDescriptor();
1053
+ var ref = new ActionReference();
1054
+ ref.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Msk '));
1055
+ desc.putReference(cTID('null'), ref);
1056
+ executeAction(cTID('Dlt '), desc, DialogModes.NO);
1057
+
1058
+ return {
1059
+ maskDeleted: true
1060
+ };
1061
+ `,
1062
+ /**
1063
+ * Apply layer mask
1064
+ */
1065
+ applyLayerMask: () => `
1066
+ ${helperFunctions}
1067
+
1068
+ if (app.documents.length === 0) {
1069
+ throw new Error('No active document');
1070
+ }
1071
+
1072
+ var desc = new ActionDescriptor();
1073
+ var ref = new ActionReference();
1074
+ ref.putEnumerated(cTID('Chnl'), cTID('Chnl'), cTID('Msk '));
1075
+ desc.putReference(cTID('null'), ref);
1076
+ executeAction(cTID('Aply'), desc, DialogModes.NO);
1077
+
1078
+ return {
1079
+ maskApplied: true
1080
+ };
1081
+ `,
1082
+ /**
1083
+ * Play an action from Actions palette
1084
+ */
1085
+ playAction: (actionName, actionSetName) => `
1086
+ app.doAction("${actionName.replace(/"/g, '\\"')}", "${actionSetName.replace(/"/g, '\\"')}");
1087
+
1088
+ return {
1089
+ action: '${actionName}',
1090
+ set: '${actionSetName}'
1091
+ };
1092
+ `,
1093
+ /**
1094
+ * Execute custom JavaScript code
1095
+ */
1096
+ executeCustomScript: (code) => `
1097
+ ${code}
1098
+ `,
1099
+ /**
1100
+ * Rasterize active layer
1101
+ */
1102
+ rasterizeLayer: () => `
1103
+ if (app.documents.length === 0) {
1104
+ throw new Error('No active document');
1105
+ }
1106
+ var layer = app.activeDocument.activeLayer;
1107
+
1108
+ if (layer.kind === LayerKind.NORMAL) {
1109
+ return {
1110
+ message: 'Layer is already rasterized',
1111
+ kind: 'NORMAL'
1112
+ };
1113
+ }
1114
+
1115
+ var originalKind = String(layer.kind);
1116
+ layer.rasterize(RasterizeType.ENTIRELAYER);
1117
+
1118
+ return {
1119
+ rasterized: true,
1120
+ originalKind: originalKind,
1121
+ newKind: 'NORMAL'
1122
+ };
1123
+ `,
1124
+ /**
1125
+ * Undo last operation (step backward in history)
1126
+ */
1127
+ undo: (steps = 1) => `
1128
+ ${getContextInfo}
1129
+
1130
+ if (app.documents.length === 0) {
1131
+ throw new Error('No active document');
1132
+ }
1133
+ var doc = app.activeDocument;
1134
+
1135
+ // Get current history state index
1136
+ var currentIndex = -1;
1137
+ for (var i = 0; i < doc.historyStates.length; i++) {
1138
+ if (doc.historyStates[i] === doc.activeHistoryState) {
1139
+ currentIndex = i;
1140
+ break;
1141
+ }
1142
+ }
1143
+
1144
+ if (currentIndex === -1) {
1145
+ throw new Error('Could not find current history state');
1146
+ }
1147
+
1148
+ // Calculate target index
1149
+ var targetIndex = Math.max(0, currentIndex - ${steps});
1150
+
1151
+ // Set active history state to go back
1152
+ if (targetIndex < doc.historyStates.length) {
1153
+ doc.activeHistoryState = doc.historyStates[targetIndex];
1154
+ }
1155
+
1156
+ var result = {
1157
+ undone: true,
1158
+ steps: currentIndex - targetIndex,
1159
+ currentHistoryState: doc.activeHistoryState.name,
1160
+ remainingStates: currentIndex - targetIndex,
1161
+ context: getContextInfo()
1162
+ };
1163
+ return result;
1164
+ `,
1165
+ /**
1166
+ * Redo operation (step forward in history)
1167
+ */
1168
+ redo: (steps = 1) => `
1169
+ ${getContextInfo}
1170
+
1171
+ if (app.documents.length === 0) {
1172
+ throw new Error('No active document');
1173
+ }
1174
+ var doc = app.activeDocument;
1175
+
1176
+ // Get current history state index
1177
+ var currentIndex = -1;
1178
+ for (var i = 0; i < doc.historyStates.length; i++) {
1179
+ if (doc.historyStates[i] === doc.activeHistoryState) {
1180
+ currentIndex = i;
1181
+ break;
1182
+ }
1183
+ }
1184
+
1185
+ if (currentIndex === -1) {
1186
+ throw new Error('Could not find current history state');
1187
+ }
1188
+
1189
+ // Calculate target index
1190
+ var targetIndex = Math.min(doc.historyStates.length - 1, currentIndex + ${steps});
1191
+
1192
+ // Set active history state to go forward
1193
+ if (targetIndex >= 0) {
1194
+ doc.activeHistoryState = doc.historyStates[targetIndex];
1195
+ }
1196
+
1197
+ var result = {
1198
+ redone: true,
1199
+ steps: targetIndex - currentIndex,
1200
+ currentHistoryState: doc.activeHistoryState.name,
1201
+ availableRedoSteps: doc.historyStates.length - 1 - targetIndex,
1202
+ context: getContextInfo()
1203
+ };
1204
+ return result;
1205
+ `,
1206
+ /**
1207
+ * Get history states
1208
+ */
1209
+ getHistoryStates: () => `
1210
+ ${getContextInfo}
1211
+
1212
+ if (app.documents.length === 0) {
1213
+ throw new Error('No active document');
1214
+ }
1215
+ var doc = app.activeDocument;
1216
+
1217
+ var states = [];
1218
+ var currentIndex = -1;
1219
+
1220
+ for (var i = 0; i < doc.historyStates.length; i++) {
1221
+ var state = doc.historyStates[i];
1222
+ states.push({
1223
+ name: state.name,
1224
+ snapshot: state.snapshot || false
1225
+ });
1226
+
1227
+ if (state === doc.activeHistoryState) {
1228
+ currentIndex = i;
1229
+ }
1230
+ }
1231
+
1232
+ var result = {
1233
+ totalStates: states.length,
1234
+ currentIndex: currentIndex,
1235
+ currentState: currentIndex >= 0 ? states[currentIndex].name : 'Unknown',
1236
+ canUndo: currentIndex > 0,
1237
+ canRedo: currentIndex < states.length - 1,
1238
+ states: states,
1239
+ context: getContextInfo()
1240
+ };
1241
+ return result;
1242
+ `,
1243
+ /**
1244
+ * Move layer to specific position (reorder)
1245
+ */
1246
+ moveLayerToPosition: (targetLayerName, position) => `
1247
+ ${getContextInfo}
1248
+
1249
+ if (app.documents.length === 0) {
1250
+ throw new Error('No active document');
1251
+ }
1252
+ var doc = app.activeDocument;
1253
+ var activeLayer = doc.activeLayer;
1254
+
1255
+ // Find target layer
1256
+ var targetLayer = null;
1257
+ for (var i = 0; i < doc.layers.length; i++) {
1258
+ if (doc.layers[i].name === "${targetLayerName.replace(/"/g, '\\"')}") {
1259
+ targetLayer = doc.layers[i];
1260
+ break;
1261
+ }
1262
+ }
1263
+
1264
+ if (!targetLayer) {
1265
+ throw new Error('Target layer not found: ${targetLayerName}');
1266
+ }
1267
+
1268
+ // Determine ElementPlacement
1269
+ var placement;
1270
+ if ("${position}" === "ABOVE") {
1271
+ placement = ElementPlacement.PLACEBEFORE;
1272
+ } else if ("${position}" === "BELOW") {
1273
+ placement = ElementPlacement.PLACEAFTER;
1274
+ } else if ("${position}" === "TOP") {
1275
+ placement = ElementPlacement.PLACEATBEGINNING;
1276
+ } else if ("${position}" === "BOTTOM") {
1277
+ placement = ElementPlacement.PLACEATEND;
1278
+ } else {
1279
+ throw new Error('Invalid position. Use: ABOVE, BELOW, TOP, or BOTTOM');
1280
+ }
1281
+
1282
+ // Move the layer
1283
+ activeLayer.move(targetLayer, placement);
1284
+
1285
+ var result = {
1286
+ moved: true,
1287
+ layerName: activeLayer.name,
1288
+ position: "${position}",
1289
+ relativeTo: targetLayer.name,
1290
+ context: getContextInfo()
1291
+ };
1292
+ return result;
1293
+ `,
1294
+ /**
1295
+ * Move layer to top of layer stack
1296
+ */
1297
+ moveLayerToTop: () => `
1298
+ ${getContextInfo}
1299
+
1300
+ if (app.documents.length === 0) {
1301
+ throw new Error('No active document');
1302
+ }
1303
+ var doc = app.activeDocument;
1304
+ var layer = doc.activeLayer;
1305
+
1306
+ if (doc.layers.length > 0) {
1307
+ layer.move(doc.layers[0], ElementPlacement.PLACEBEFORE);
1308
+ }
1309
+
1310
+ var result = {
1311
+ moved: true,
1312
+ layerName: layer.name,
1313
+ position: 'top',
1314
+ context: getContextInfo()
1315
+ };
1316
+ return result;
1317
+ `,
1318
+ /**
1319
+ * Move layer to bottom of layer stack
1320
+ */
1321
+ moveLayerToBottom: () => `
1322
+ ${getContextInfo}
1323
+
1324
+ if (app.documents.length === 0) {
1325
+ throw new Error('No active document');
1326
+ }
1327
+ var doc = app.activeDocument;
1328
+ var layer = doc.activeLayer;
1329
+
1330
+ if (doc.layers.length > 0) {
1331
+ layer.move(doc.layers[doc.layers.length - 1], ElementPlacement.PLACEAFTER);
1332
+ }
1333
+
1334
+ var result = {
1335
+ moved: true,
1336
+ layerName: layer.name,
1337
+ position: 'bottom',
1338
+ context: getContextInfo()
1339
+ };
1340
+ return result;
1341
+ `,
1342
+ /**
1343
+ * Move layer up one position
1344
+ */
1345
+ moveLayerUp: () => `
1346
+ ${getContextInfo}
1347
+
1348
+ if (app.documents.length === 0) {
1349
+ throw new Error('No active document');
1350
+ }
1351
+ var doc = app.activeDocument;
1352
+ var layer = doc.activeLayer;
1353
+
1354
+ // Find current layer index
1355
+ var currentIndex = -1;
1356
+ for (var i = 0; i < doc.layers.length; i++) {
1357
+ if (doc.layers[i] === layer) {
1358
+ currentIndex = i;
1359
+ break;
1360
+ }
1361
+ }
1362
+
1363
+ if (currentIndex <= 0) {
1364
+ return {
1365
+ moved: false,
1366
+ message: 'Layer is already at the top',
1367
+ context: getContextInfo()
1368
+ };
1369
+ }
1370
+
1371
+ // Move before the layer above
1372
+ layer.move(doc.layers[currentIndex - 1], ElementPlacement.PLACEBEFORE);
1373
+
1374
+ var result = {
1375
+ moved: true,
1376
+ layerName: layer.name,
1377
+ direction: 'up',
1378
+ context: getContextInfo()
1379
+ };
1380
+ return result;
1381
+ `,
1382
+ /**
1383
+ * Move layer down one position
1384
+ */
1385
+ moveLayerDown: () => `
1386
+ ${getContextInfo}
1387
+
1388
+ if (app.documents.length === 0) {
1389
+ throw new Error('No active document');
1390
+ }
1391
+ var doc = app.activeDocument;
1392
+ var layer = doc.activeLayer;
1393
+
1394
+ // Find current layer index
1395
+ var currentIndex = -1;
1396
+ for (var i = 0; i < doc.layers.length; i++) {
1397
+ if (doc.layers[i] === layer) {
1398
+ currentIndex = i;
1399
+ break;
1400
+ }
1401
+ }
1402
+
1403
+ if (currentIndex === -1 || currentIndex >= doc.layers.length - 1) {
1404
+ return {
1405
+ moved: false,
1406
+ message: 'Layer is already at the bottom',
1407
+ context: getContextInfo()
1408
+ };
1409
+ }
1410
+
1411
+ // Move after the layer below
1412
+ layer.move(doc.layers[currentIndex + 1], ElementPlacement.PLACEAFTER);
1413
+
1414
+ var result = {
1415
+ moved: true,
1416
+ layerName: layer.name,
1417
+ direction: 'down',
1418
+ context: getContextInfo()
1419
+ };
1420
+ return result;
1421
+ `,
1422
+ };
1423
+ /**
1424
+ * Generate ExtendScript code with error handling
1425
+ */
1426
+ export function generateExtendScript(code) {
1427
+ return `
1428
+ (function() {
1429
+ try {
1430
+ ${code}
1431
+ } catch (error) {
1432
+ return 'ERROR: ' + (error.message || String(error));
1433
+ }
1434
+ })();
1435
+ `.trim();
1436
+ }
1437
+ //# sourceMappingURL=extendscript.js.map