@synthetic-ai/photoshop-mcp 1.0.7-dev.0 → 1.0.8-dev.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 (2) hide show
  1. package/package.json +1 -1
  2. package/server.mjs +555 -555
package/server.mjs CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Photoshop MCP — Synthetic
4
- * Puente WebSocket ↔ plugin CEP/UXP (PS-MCP Bridge). El plugin corre dentro de
5
- * Photoshop y ejecuta ExtendScript / Action Manager; este server expone las tools.
4
+ * WebSocket bridge ↔ CEP/UXP plugin (PS-MCP Bridge). The plugin runs inside
5
+ * Photoshop and executes ExtendScript / Action Manager; this server exposes the tools.
6
6
  *
7
- * Convención (estilo Synthetic / After Effects MCP):
8
- * - Diagnóstico/app: ps_healthcheck, ps_info, ps_help
7
+ * Convention (Synthetic / After Effects MCP style):
8
+ * - Diagnostics/app: ps_healthcheck, ps_info, ps_help
9
9
  * - Escape hatches: run_jsx, run_jsx_json, run_action
10
- * - Dominio: verbo_sustantivo en snake_case, sin prefijo
10
+ * - Domain: verb_noun in snake_case, no prefix
11
11
  * (list_layers, add_text_layer, set_layer_opacity, export_png…)
12
- * - Bulk: batch_* cuando aplica
12
+ * - Bulk: batch_* where applicable
13
13
  */
14
14
 
15
15
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -138,7 +138,7 @@ connectBridge();
138
138
 
139
139
  function rpc(cmd, params = {}, timeoutMs = 120000) {
140
140
  return new Promise((resolve) => {
141
- if (!sock || sock.readyState !== 1) { resolve({ ok: false, error: "Photoshop no conectado. Abrí Photoshop con el panel «PS-MCP Bridge» (que hostea el bridge en el puerto " + PORT + ")." }); return; }
141
+ if (!sock || sock.readyState !== 1) { resolve({ ok: false, error: "Photoshop not connected. Open Photoshop with the «PS-MCP Bridge» panel (which hosts the bridge on port " + PORT + ")." }); return; }
142
142
  const id = nextId++;
143
143
  const to = setTimeout(() => { pending.delete(id); resolve({ ok: false, error: "timeout (" + timeoutMs + "ms)" }); }, timeoutMs);
144
144
  pending.set(id, { resolve, to });
@@ -150,9 +150,9 @@ const text = (o) => ({ content: [{ type: "text", text: typeof o === "string" ? o
150
150
  /* ---------------- prelude ExtendScript (helpers) ---------------- */
151
151
  const HELPERS = [
152
152
  "app.preferences.rulerUnits=Units.PIXELS;",
153
- "function _doc(){if(!app.documents.length)throw 'No hay documento abierto';return app.activeDocument;}",
153
+ "function _doc(){if(!app.documents.length)throw 'No document open';return app.activeDocument;}",
154
154
  "function _find(n){function r(ls){for(var i=0;i<ls.length;i++){var L=ls[i];if(L.name===n)return L;if(L.typename==='LayerSet'){var x=r(L.layers);if(x)return x;}}return null;}return r(app.activeDocument.layers);}",
155
- "function _need(n){var L=_find(n);if(!L)throw 'No se encontró la capa: '+n;return L;}",
155
+ "function _need(n){var L=_find(n);if(!L)throw 'Layer not found: '+n;return L;}",
156
156
  "function _hex(h){h=String(h).replace('#','');var c=new SolidColor();c.rgb.hexValue=h;return c;}",
157
157
  "function _rgb(h){h=String(h).replace('#','');return {r:parseInt(h.substring(0,2),16),g:parseInt(h.substring(2,4),16),b:parseInt(h.substring(4,6),16)};}",
158
158
  "function _sel(L){app.activeDocument.activeLayer=L;}",
@@ -172,7 +172,7 @@ const HELPERS = [
172
172
 
173
173
  const server = new McpServer(
174
174
  { name: "photoshop", version: "1.0.0" },
175
- { instructions: "Photoshop MCP (Synthetic). Controlá Adobe Photoshop por un plugin puente. Convención de tools: diagnóstico ps_* ; dominio verbo_sustantivo (list_layers, add_text_layer, export_png) ; escape hatches run_jsx / run_jsx_json / run_action. Empezá con ps_healthcheck. Usá ps_help para ver el catálogo por categoría." }
175
+ { instructions: "Photoshop MCP (Synthetic). Control Adobe Photoshop through a bridge plugin. Tool convention: diagnostics ps_* ; domain verb_noun (list_layers, add_text_layer, export_png) ; escape hatches run_jsx / run_jsx_json / run_action. Start with ps_healthcheck. Use ps_help to see the catalog by category." }
176
176
  );
177
177
 
178
178
  // jsxTool: corre un snippet ExtendScript (con HELPERS) por el puente.
@@ -188,25 +188,25 @@ function jsxTool(name, description, schema, build, timeout = 120000) {
188
188
  const S = z;
189
189
 
190
190
  /* ====================== DIAGNÓSTICO / APP ====================== */
191
- server.tool("ps_healthcheck", "Verifica conexión con Photoshop: versión y documento activo.", {}, async () => {
192
- if (!sock || sock.readyState !== 1) return text({ connected: false, port: PORT, hint: "Abrí Photoshop con el panel PS-MCP Bridge (hostea el bridge)." });
191
+ server.tool("ps_healthcheck", "Checks the connection to Photoshop: version and active document.", {}, async () => {
192
+ if (!sock || sock.readyState !== 1) return text({ connected: false, port: PORT, hint: "Open Photoshop with the PS-MCP Bridge panel (it hosts the bridge)." });
193
193
  return text({ connected: true, port: PORT, ...(await rpc("health", {}, 15000)) });
194
194
  });
195
- jsxTool("ps_info", "Info de la app: versión, build, ruta, documentos abiertos.", {},
195
+ jsxTool("ps_info", "App info: version, build, path, open documents.", {},
196
196
  "return {version:app.version, build:app.build!==undefined?app.build:'', path:app.path? app.path.fsName:'', openDocuments:app.documents.length, foregroundColor: (function(){try{return '#'+app.foregroundColor.rgb.hexValue;}catch(e){return '';}})()};");
197
- server.tool("run_jsx", "Ejecuta ExtendScript arbitrario en Photoshop. El código puede usar 'return'. Devuelve {ok,data}.", { code: S.string() },
197
+ server.tool("run_jsx", "Runs arbitrary ExtendScript in Photoshop. The code can use 'return'. Returns {ok,data}.", { code: S.string() },
198
198
  async ({ code }) => {
199
199
  const lic = await checkLicense();
200
200
  if (!lic.valid) return licenseErrorResponse("run_jsx");
201
201
  return text(await rpc("eval", { code }, 600000));
202
202
  });
203
- server.tool("run_jsx_json", "Ejecuta ExtendScript que devuelve un valor serializable (JSON). Igual que run_jsx pero pensado para datos.", { code: S.string() },
203
+ server.tool("run_jsx_json", "Runs ExtendScript that returns a JSON-serializable value. Same as run_jsx but meant for data.", { code: S.string() },
204
204
  async ({ code }) => {
205
205
  const lic = await checkLicense();
206
206
  if (!lic.valid) return licenseErrorResponse("run_jsx_json");
207
207
  return text(await rpc("eval", { code }, 600000));
208
208
  });
209
- server.tool("run_action", "Ejecuta una acción de Action Manager (batchPlay/executeAction). Pasá descriptores. Escape hatch de bajo nivel.", { commands: S.array(S.any()), options: S.any().optional() },
209
+ server.tool("run_action", "Runs an Action Manager action (batchPlay/executeAction). Pass descriptors. Low-level escape hatch.", { commands: S.array(S.any()), options: S.any().optional() },
210
210
  async ({ commands, options }) => {
211
211
  const lic = await checkLicense();
212
212
  if (!lic.valid) return licenseErrorResponse("run_action");
@@ -214,552 +214,552 @@ server.tool("run_action", "Ejecuta una acción de Action Manager (batchPlay/exec
214
214
  });
215
215
 
216
216
  /* ====================== DOCUMENTOS ====================== */
217
- jsxTool("list_documents", "Lista los documentos abiertos.", {},
217
+ jsxTool("list_documents", "Lists the open documents.", {},
218
218
  "var a=[];for(var i=0;i<app.documents.length;i++)a.push(app.documents[i].name);return a;");
219
- jsxTool("get_document_info", "Info del documento activo: nombre, tamaño, resolución, modo, bits, total de capas.", {},
219
+ jsxTool("get_document_info", "Active document info: name, size, resolution, mode, bit depth, total layers.", {},
220
220
  "var d=_doc();var total=d.layers.length;try{var ref=new ActionReference();ref.putProperty(charIDToTypeID('Prpr'),stringIDToTypeID('numberOfLayers'));ref.putEnumerated(charIDToTypeID('Dcmn'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));total=executeActionGet(ref).getInteger(stringIDToTypeID('numberOfLayers'));}catch(e){}var path=null;try{path=d.path.fsName;}catch(e){}return {name:d.name,width:Math.round(d.width),height:Math.round(d.height),resolution:Math.round(d.resolution),mode:String(d.mode).replace('DocumentMode.',''),bitsPerChannel:String(d.bitsPerChannel).replace('BitsPerChannelType.',''),layersTotal:total,path:path};");
221
- jsxTool("create_document", "Crea un documento nuevo.", { width: S.number(), height: S.number(), resolution: S.number().optional(), name: S.string().optional() },
222
- "var d=app.documents.add(UnitValue(A.width,'px'),UnitValue(A.height,'px'),A.resolution||72,A.name||'Untitled',NewDocumentMode.RGB,DocumentFill.TRANSPARENT);return 'creado: '+d.name;");
223
- jsxTool("open_document", "Abre un archivo.", { path: S.string() },
224
- "app.open(new File(A.path));return 'abierto: '+app.activeDocument.name;");
225
- jsxTool("save_document", "Guarda el documento activo.", {}, "_doc().save();return 'guardado';");
226
- jsxTool("save_document_as", "Guarda como PSD en la ruta dada.", { path: S.string() },
227
- "var o=new PhotoshopSaveOptions();_doc().saveAs(new File(A.path),o,true);return 'guardado: '+A.path;");
228
- jsxTool("close_document", "Cierra el documento activo (save=true para guardar).", { save: S.boolean().optional() },
229
- "_doc().close(A.save?SaveOptions.SAVECHANGES:SaveOptions.DONOTSAVECHANGES);return 'cerrado';");
230
- jsxTool("set_active_document", "Activa un documento por nombre.", { name: S.string() },
231
- "app.activeDocument=app.documents.getByName(A.name);return 'activo: '+A.name;");
232
- jsxTool("duplicate_document", "Duplica el documento activo.", { name: S.string().optional() },
233
- "var d=_doc().duplicate(A.name||undefined);return 'duplicado: '+d.name;");
234
- jsxTool("flatten_document", "Aplana el documento activo.", {}, "_doc().flatten();return 'aplanado';");
235
- jsxTool("resize_image", "Redimensiona la imagen (px).", { width: S.number().optional(), height: S.number().optional(), resolution: S.number().optional() },
236
- "var d=_doc();d.resizeImage(A.width?UnitValue(A.width,'px'):undefined,A.height?UnitValue(A.height,'px'):undefined,A.resolution||undefined);return 'imagen '+Math.round(d.width)+'x'+Math.round(d.height);");
237
- jsxTool("resize_canvas", "Redimensiona el lienzo (px).", { width: S.number(), height: S.number() },
238
- "_doc().resizeCanvas(UnitValue(A.width,'px'),UnitValue(A.height,'px'));return 'lienzo '+A.width+'x'+A.height;");
239
- jsxTool("crop_document", "Recorta el documento a un rectángulo (px).", { x: S.number(), y: S.number(), width: S.number(), height: S.number() },
240
- "_doc().crop([A.x,A.y,A.x+A.width,A.y+A.height]);return 'recortado';");
241
- jsxTool("trim_document", "Recorta bordes (transparent | topleft | bottomright).", { type: S.string().optional() },
221
+ jsxTool("create_document", "Creates a new document.", { width: S.number(), height: S.number(), resolution: S.number().optional(), name: S.string().optional() },
222
+ "var d=app.documents.add(UnitValue(A.width,'px'),UnitValue(A.height,'px'),A.resolution||72,A.name||'Untitled',NewDocumentMode.RGB,DocumentFill.TRANSPARENT);return 'created: '+d.name;");
223
+ jsxTool("open_document", "Opens a file.", { path: S.string() },
224
+ "app.open(new File(A.path));return 'opened: '+app.activeDocument.name;");
225
+ jsxTool("save_document", "Saves the active document.", {}, "_doc().save();return 'saved';");
226
+ jsxTool("save_document_as", "Saves as PSD at the given path.", { path: S.string() },
227
+ "var o=new PhotoshopSaveOptions();_doc().saveAs(new File(A.path),o,true);return 'saved: '+A.path;");
228
+ jsxTool("close_document", "Closes the active document (save=true to save).", { save: S.boolean().optional() },
229
+ "_doc().close(A.save?SaveOptions.SAVECHANGES:SaveOptions.DONOTSAVECHANGES);return 'closed';");
230
+ jsxTool("set_active_document", "Activates a document by name.", { name: S.string() },
231
+ "app.activeDocument=app.documents.getByName(A.name);return 'active: '+A.name;");
232
+ jsxTool("duplicate_document", "Duplicates the active document.", { name: S.string().optional() },
233
+ "var d=_doc().duplicate(A.name||undefined);return 'duplicated: '+d.name;");
234
+ jsxTool("flatten_document", "Flattens the active document.", {}, "_doc().flatten();return 'flattened';");
235
+ jsxTool("resize_image", "Resizes the image (px).", { width: S.number().optional(), height: S.number().optional(), resolution: S.number().optional() },
236
+ "var d=_doc();d.resizeImage(A.width?UnitValue(A.width,'px'):undefined,A.height?UnitValue(A.height,'px'):undefined,A.resolution||undefined);return 'image '+Math.round(d.width)+'x'+Math.round(d.height);");
237
+ jsxTool("resize_canvas", "Resizes the canvas (px).", { width: S.number(), height: S.number() },
238
+ "_doc().resizeCanvas(UnitValue(A.width,'px'),UnitValue(A.height,'px'));return 'canvas '+A.width+'x'+A.height;");
239
+ jsxTool("crop_document", "Crops the document to a rectangle (px).", { x: S.number(), y: S.number(), width: S.number(), height: S.number() },
240
+ "_doc().crop([A.x,A.y,A.x+A.width,A.y+A.height]);return 'cropped';");
241
+ jsxTool("trim_document", "Trims edges (transparent | topleft | bottomright).", { type: S.string().optional() },
242
242
  "var t=A.type==='topleft'?TrimType.TOPLEFT:(A.type==='bottomright'?TrimType.BOTTOMRIGHT:TrimType.TRANSPARENT);_doc().trim(t,true,true,true,true);return 'trim';");
243
- jsxTool("rotate_canvas", "Rota el lienzo (grados).", { angle: S.number() }, "_doc().rotateCanvas(A.angle);return 'rotado '+A.angle;");
244
- jsxTool("revert_document", "Revierte el documento al último guardado.", {}, "executeAction(charIDToTypeID('Rvrt'),undefined,DialogModes.NO);return 'revertido';");
245
- jsxTool("purge", "Purga cachés (history/clipboard/all).", {}, "app.purge(PurgeTarget.ALLCACHES);return 'purgado';");
246
- jsxTool("place_file", "Coloca un archivo como Smart Object.", { path: S.string() },
247
- "var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));d.putEnumerated(charIDToTypeID('FTcs'),charIDToTypeID('QCSt'),charIDToTypeID('Qcsa'));executeAction(charIDToTypeID('Plc '),d,DialogModes.NO);return 'colocado';");
243
+ jsxTool("rotate_canvas", "Rotates the canvas (degrees).", { angle: S.number() }, "_doc().rotateCanvas(A.angle);return 'rotated '+A.angle;");
244
+ jsxTool("revert_document", "Reverts the document to the last save.", {}, "executeAction(charIDToTypeID('Rvrt'),undefined,DialogModes.NO);return 'reverted';");
245
+ jsxTool("purge", "Purges caches (history/clipboard/all).", {}, "app.purge(PurgeTarget.ALLCACHES);return 'purged';");
246
+ jsxTool("place_file", "Places a file as a Smart Object.", { path: S.string() },
247
+ "var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));d.putEnumerated(charIDToTypeID('FTcs'),charIDToTypeID('QCSt'),charIDToTypeID('Qcsa'));executeAction(charIDToTypeID('Plc '),d,DialogModes.NO);return 'placed';");
248
248
 
249
249
  /* ====================== CAPAS ====================== */
250
- jsxTool("list_layers", "Árbol de capas (opcional: group para acotar a una carpeta). En docs grandes acotá con group.", { group: S.string().optional() },
250
+ jsxTool("list_layers", "Layer tree (optional: group to scope to a folder). On large docs, scope with group.", { group: S.string().optional() },
251
251
  "function kind(L){if(L.typename==='LayerSet')return 'group';try{return String(L.kind).replace('LayerKind.','');}catch(e){return 'layer';}}function tree(ls){var a=[];for(var i=0;i<ls.length;i++){var L=ls[i];var n={name:L.name,type:kind(L)};try{n.visible=L.visible;}catch(e){}try{n.opacity=Math.round(L.opacity);}catch(e){}if(L.typename==='LayerSet')n.children=tree(L.layers);a.push(n);}return a;}var root=A.group?_need(A.group).layers:_doc().layers;return tree(root);");
252
- jsxTool("get_layer", "Detalle de una capa por nombre.", { name: S.string() },
252
+ jsxTool("get_layer", "Details of a layer by name.", { name: S.string() },
253
253
  "var L=_need(A.name);var isG=(L.typename==='LayerSet');var o={name:L.name,type:isG?'group':String(L.kind).replace('LayerKind.','')};try{o.visible=L.visible;}catch(e){}try{o.opacity=Math.round(L.opacity);}catch(e){}try{o.blend=String(L.blendMode).replace('BlendMode.','');}catch(e){}try{var b=L.bounds;o.bounds=[Math.round(b[0]),Math.round(b[1]),Math.round(b[2]),Math.round(b[3])];}catch(e){}try{if(L.kind==LayerKind.TEXT){o.text=String(L.textItem.contents);o.font=String(L.textItem.font);o.size=Math.round(parseFloat(L.textItem.size)*100)/100;}}catch(e){}return o;");
254
- jsxTool("add_text_layer", "Crea una capa de texto.", { text: S.string(), x: S.number().optional(), y: S.number().optional(), size: S.number().optional(), font: S.string().optional(), color: S.string().optional(), name: S.string().optional() },
255
- "var d=_doc();var L=d.artLayers.add();L.kind=LayerKind.TEXT;var t=L.textItem;t.contents=A.text;t.position=[A.x||0,A.y||0];if(A.size)t.size=A.size;if(A.font)t.font=A.font;if(A.color)t.color=_hex(A.color);if(A.name)L.name=A.name;return 'texto: '+L.name;");
256
- jsxTool("add_color_layer", "Crea una capa rellena de un color.", { color: S.string(), name: S.string().optional() },
257
- "var d=_doc();var L=d.artLayers.add();if(A.name)L.name=A.name;d.selection.selectAll();d.selection.fill(_hex(A.color));d.selection.deselect();return 'capa color: '+L.name;");
258
- jsxTool("create_group", "Crea un grupo (carpeta) vacío.", { name: S.string().optional() },
259
- "var g=_doc().layerSets.add();if(A.name)g.name=A.name;return 'grupo: '+g.name;");
260
- jsxTool("delete_layer", "Elimina una capa por nombre.", { name: S.string() }, "_need(A.name).remove();return 'eliminada';");
261
- jsxTool("duplicate_layer", "Duplica una capa.", { name: S.string(), newName: S.string().optional() },
262
- "var n=_need(A.name).duplicate();if(A.newName)n.name=A.newName;return 'duplicada: '+n.name;");
263
- jsxTool("rename_layer", "Renombra una capa.", { name: S.string(), newName: S.string() }, "_need(A.name).name=A.newName;return 'ok';");
264
- jsxTool("set_layer_visibility", "Muestra/oculta una capa.", { name: S.string(), visible: S.boolean() }, "_need(A.name).visible=!!A.visible;return 'ok';");
265
- jsxTool("set_layer_opacity", "Opacidad de capa (0-100).", { name: S.string(), opacity: S.number() }, "_need(A.name).opacity=A.opacity;return 'ok';");
266
- jsxTool("set_layer_blend_mode", "Modo de fusión (NORMAL, MULTIPLY, SCREEN, OVERLAY…).", { name: S.string(), mode: S.string() }, "_need(A.name).blendMode=BlendMode[A.mode];return 'ok';");
267
- jsxTool("translate_layer", "Mueve una capa (dx,dy px).", { name: S.string(), dx: S.number(), dy: S.number() }, "_need(A.name).translate(A.dx||0,A.dy||0);return 'ok';");
268
- jsxTool("rasterize_layer", "Rasteriza una capa.", { name: S.string() }, "_need(A.name).rasterize(RasterizeType.ENTIRELAYER);return 'ok';");
269
- jsxTool("select_layer", "Hace activa una capa.", { name: S.string() }, "_sel(_need(A.name));return 'activa: '+A.name;");
270
- jsxTool("merge_down", "Fusiona una capa con la de abajo.", { name: S.string() }, "var r=_need(A.name).merge();return 'merge: '+r.name;");
271
- jsxTool("merge_visible", "Fusiona todas las capas visibles.", {}, "_doc().mergeVisibleLayers();return 'ok';");
272
- jsxTool("merge_group", "Fusiona un grupo en una sola capa.", { name: S.string() }, "var g=_need(A.name);if(g.typename!=='LayerSet')throw 'no es grupo';return 'fusionado: '+g.merge().name;");
254
+ jsxTool("add_text_layer", "Creates a text layer.", { text: S.string(), x: S.number().optional(), y: S.number().optional(), size: S.number().optional(), font: S.string().optional(), color: S.string().optional(), name: S.string().optional() },
255
+ "var d=_doc();var L=d.artLayers.add();L.kind=LayerKind.TEXT;var t=L.textItem;t.contents=A.text;t.position=[A.x||0,A.y||0];if(A.size)t.size=A.size;if(A.font)t.font=A.font;if(A.color)t.color=_hex(A.color);if(A.name)L.name=A.name;return 'text: '+L.name;");
256
+ jsxTool("add_color_layer", "Creates a layer filled with a color.", { color: S.string(), name: S.string().optional() },
257
+ "var d=_doc();var L=d.artLayers.add();if(A.name)L.name=A.name;d.selection.selectAll();d.selection.fill(_hex(A.color));d.selection.deselect();return 'color layer: '+L.name;");
258
+ jsxTool("create_group", "Creates an empty group (folder).", { name: S.string().optional() },
259
+ "var g=_doc().layerSets.add();if(A.name)g.name=A.name;return 'group: '+g.name;");
260
+ jsxTool("delete_layer", "Deletes a layer by name.", { name: S.string() }, "_need(A.name).remove();return 'deleted';");
261
+ jsxTool("duplicate_layer", "Duplicates a layer.", { name: S.string(), newName: S.string().optional() },
262
+ "var n=_need(A.name).duplicate();if(A.newName)n.name=A.newName;return 'duplicated: '+n.name;");
263
+ jsxTool("rename_layer", "Renames a layer.", { name: S.string(), newName: S.string() }, "_need(A.name).name=A.newName;return 'ok';");
264
+ jsxTool("set_layer_visibility", "Shows/hides a layer.", { name: S.string(), visible: S.boolean() }, "_need(A.name).visible=!!A.visible;return 'ok';");
265
+ jsxTool("set_layer_opacity", "Layer opacity (0-100).", { name: S.string(), opacity: S.number() }, "_need(A.name).opacity=A.opacity;return 'ok';");
266
+ jsxTool("set_layer_blend_mode", "Blend mode (NORMAL, MULTIPLY, SCREEN, OVERLAY…).", { name: S.string(), mode: S.string() }, "_need(A.name).blendMode=BlendMode[A.mode];return 'ok';");
267
+ jsxTool("translate_layer", "Moves a layer (dx,dy px).", { name: S.string(), dx: S.number(), dy: S.number() }, "_need(A.name).translate(A.dx||0,A.dy||0);return 'ok';");
268
+ jsxTool("rasterize_layer", "Rasterizes a layer.", { name: S.string() }, "_need(A.name).rasterize(RasterizeType.ENTIRELAYER);return 'ok';");
269
+ jsxTool("select_layer", "Makes a layer active.", { name: S.string() }, "_sel(_need(A.name));return 'active: '+A.name;");
270
+ jsxTool("merge_down", "Merges a layer with the one below.", { name: S.string() }, "var r=_need(A.name).merge();return 'merge: '+r.name;");
271
+ jsxTool("merge_visible", "Merges all visible layers.", {}, "_doc().mergeVisibleLayers();return 'ok';");
272
+ jsxTool("merge_group", "Merges a group into a single layer.", { name: S.string() }, "var g=_need(A.name);if(g.typename!=='LayerSet')throw 'not a group';return 'merged: '+g.merge().name;");
273
273
 
274
274
  /* ====================== TEXTO ====================== */
275
- jsxTool("set_text_contents", "Cambia el texto de una capa de texto.", { name: S.string(), text: S.string() }, "_need(A.name).textItem.contents=A.text;return 'ok';");
276
- jsxTool("set_text_style", "Fuente/tamaño/color de una capa de texto.", { name: S.string(), size: S.number().optional(), font: S.string().optional(), color: S.string().optional() },
275
+ jsxTool("set_text_contents", "Changes the text of a text layer.", { name: S.string(), text: S.string() }, "_need(A.name).textItem.contents=A.text;return 'ok';");
276
+ jsxTool("set_text_style", "Font/size/color of a text layer.", { name: S.string(), size: S.number().optional(), font: S.string().optional(), color: S.string().optional() },
277
277
  "var t=_need(A.name).textItem;if(A.size)t.size=A.size;if(A.font)t.font=A.font;if(A.color)t.color=_hex(A.color);return 'ok';");
278
278
 
279
279
  /* ====================== COLOR / RELLENO ====================== */
280
- jsxTool("fill_layer", "Rellena la capa (activa o nombrada) con un color.", { color: S.string(), name: S.string().optional() },
280
+ jsxTool("fill_layer", "Fills the layer (active or named) with a color.", { color: S.string(), name: S.string().optional() },
281
281
  "var d=_doc();if(A.name)_sel(_need(A.name));d.selection.selectAll();d.selection.fill(_hex(A.color));d.selection.deselect();return 'ok';");
282
- jsxTool("set_foreground_color", "Color frontal.", { color: S.string() }, "app.foregroundColor=_hex(A.color);return 'ok';");
282
+ jsxTool("set_foreground_color", "Foreground color.", { color: S.string() }, "app.foregroundColor=_hex(A.color);return 'ok';");
283
283
 
284
284
  /* ====================== SELECCIÓN ====================== */
285
- jsxTool("select_all", "Selecciona todo.", {}, "_doc().selection.selectAll();return 'ok';");
286
- jsxTool("deselect", "Deselecciona.", {}, "_doc().selection.deselect();return 'ok';");
287
- jsxTool("select_rectangle", "Selección rectangular (px).", { x: S.number(), y: S.number(), width: S.number(), height: S.number() },
285
+ jsxTool("select_all", "Selects everything.", {}, "_doc().selection.selectAll();return 'ok';");
286
+ jsxTool("deselect", "Deselects.", {}, "_doc().selection.deselect();return 'ok';");
287
+ jsxTool("select_rectangle", "Rectangular selection (px).", { x: S.number(), y: S.number(), width: S.number(), height: S.number() },
288
288
  "var d=_doc();d.selection.select([[A.x,A.y],[A.x+A.width,A.y],[A.x+A.width,A.y+A.height],[A.x,A.y+A.height]]);return 'ok';");
289
- jsxTool("invert_selection", "Invierte la selección.", {}, "_doc().selection.invert();return 'ok';");
290
- jsxTool("expand_selection", "Expande la selección (px).", { pixels: S.number() }, "_doc().selection.expand(A.pixels);return 'ok';");
291
- jsxTool("feather_selection", "Difumina la selección (px).", { pixels: S.number() }, "_doc().selection.feather(A.pixels);return 'ok';");
292
- jsxTool("fill_selection", "Rellena la selección con un color.", { color: S.string() }, "_doc().selection.fill(_hex(A.color));return 'ok';");
293
- jsxTool("stroke_selection", "Traza el borde de la selección.", { color: S.string(), width: S.number().optional() }, "_doc().selection.stroke(_hex(A.color),A.width||1,StrokeLocation.CENTER);return 'ok';");
294
- jsxTool("select_layer_pixels", "Carga la transparencia de una capa como selección.", { name: S.string() },
289
+ jsxTool("invert_selection", "Inverts the selection.", {}, "_doc().selection.invert();return 'ok';");
290
+ jsxTool("expand_selection", "Expands the selection (px).", { pixels: S.number() }, "_doc().selection.expand(A.pixels);return 'ok';");
291
+ jsxTool("feather_selection", "Feathers the selection (px).", { pixels: S.number() }, "_doc().selection.feather(A.pixels);return 'ok';");
292
+ jsxTool("fill_selection", "Fills the selection with a color.", { color: S.string() }, "_doc().selection.fill(_hex(A.color));return 'ok';");
293
+ jsxTool("stroke_selection", "Strokes the edge of the selection.", { color: S.string(), width: S.number().optional() }, "_doc().selection.stroke(_hex(A.color),A.width||1,StrokeLocation.CENTER);return 'ok';");
294
+ jsxTool("select_layer_pixels", "Loads a layer's transparency as a selection.", { name: S.string() },
295
295
  "_sel(_need(A.name));var d=new ActionDescriptor();var r=new ActionReference();r.putProperty(charIDToTypeID('Chnl'),charIDToTypeID('fsel'));d.putReference(charIDToTypeID('null'),r);var r2=new ActionReference();r2.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Chnl'),charIDToTypeID('Trsp'));d.putReference(charIDToTypeID('T '),r2);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok';");
296
296
 
297
297
  /* ====================== MÁSCARAS ====================== */
298
- jsxTool("add_layer_mask", "Agrega máscara de capa (reveal all, o hide=true para ocultar todo).", { name: S.string(), hide: S.boolean().optional() },
299
- "_sel(_need(A.name));var d=new ActionDescriptor();d.putClass(charIDToTypeID('Nw '),charIDToTypeID('Chnl'));var r=new ActionReference();r.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Chnl'),charIDToTypeID('Msk '));d.putReference(charIDToTypeID('At '),r);d.putEnumerated(charIDToTypeID('Usng'),charIDToTypeID('UsrM'),A.hide?charIDToTypeID('HdAl'):charIDToTypeID('RvlA'));executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'máscara';");
298
+ jsxTool("add_layer_mask", "Adds a layer mask (reveal all, or hide=true to hide everything).", { name: S.string(), hide: S.boolean().optional() },
299
+ "_sel(_need(A.name));var d=new ActionDescriptor();d.putClass(charIDToTypeID('Nw '),charIDToTypeID('Chnl'));var r=new ActionReference();r.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Chnl'),charIDToTypeID('Msk '));d.putReference(charIDToTypeID('At '),r);d.putEnumerated(charIDToTypeID('Usng'),charIDToTypeID('UsrM'),A.hide?charIDToTypeID('HdAl'):charIDToTypeID('RvlA'));executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'mask';");
300
300
 
301
301
  /* ====================== TRANSFORM (capa) ====================== */
302
- jsxTool("scale_layer", "Escala una capa (porcentaje).", { name: S.string(), width: S.number(), height: S.number() },
302
+ jsxTool("scale_layer", "Scales a layer (percentage).", { name: S.string(), width: S.number(), height: S.number() },
303
303
  "_need(A.name).resize(A.width,A.height,AnchorPosition.MIDDLECENTER);return 'ok';");
304
- jsxTool("rotate_layer", "Rota una capa (grados).", { name: S.string(), angle: S.number() },
304
+ jsxTool("rotate_layer", "Rotates a layer (degrees).", { name: S.string(), angle: S.number() },
305
305
  "_need(A.name).rotate(A.angle,AnchorPosition.MIDDLECENTER);return 'ok';");
306
- jsxTool("flip_layer", "Voltea una capa (axis: horizontal | vertical).", { name: S.string(), axis: S.string().optional() },
306
+ jsxTool("flip_layer", "Flips a layer (axis: horizontal | vertical).", { name: S.string(), axis: S.string().optional() },
307
307
  "_sel(_need(A.name));var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);d.putEnumerated(stringIDToTypeID('axis'),stringIDToTypeID('orientation'),A.axis==='vertical'?stringIDToTypeID('vertical'):stringIDToTypeID('horizontal'));executeAction(stringIDToTypeID('flip'),d,DialogModes.NO);return 'ok';");
308
308
 
309
309
  /* ====================== FILTROS ====================== */
310
- jsxTool("apply_gaussian_blur", "Desenfoque gaussiano (radio px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyGaussianBlur(A.radius);return 'ok';");
311
- jsxTool("apply_sharpen", "Enfocar.", { name: S.string().optional() }, "_act(A.name).applySharpen();return 'ok';");
312
- jsxTool("apply_unsharp_mask", "Máscara de enfoque.", { amount: S.number(), radius: S.number(), threshold: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyUnSharpMask(A.amount,A.radius,A.threshold||0);return 'ok';");
313
- jsxTool("apply_motion_blur", "Desenfoque de movimiento.", { angle: S.number(), distance: S.number(), name: S.string().optional() }, "_act(A.name).applyMotionBlur(A.angle,A.distance);return 'ok';");
314
- jsxTool("apply_add_noise", "Agrega ruido.", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applyAddNoise(A.amount,NoiseDistribution.GAUSSIAN,false);return 'ok';");
310
+ jsxTool("apply_gaussian_blur", "Gaussian blur (radius px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyGaussianBlur(A.radius);return 'ok';");
311
+ jsxTool("apply_sharpen", "Sharpens.", { name: S.string().optional() }, "_act(A.name).applySharpen();return 'ok';");
312
+ jsxTool("apply_unsharp_mask", "Unsharp mask.", { amount: S.number(), radius: S.number(), threshold: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyUnSharpMask(A.amount,A.radius,A.threshold||0);return 'ok';");
313
+ jsxTool("apply_motion_blur", "Motion blur.", { angle: S.number(), distance: S.number(), name: S.string().optional() }, "_act(A.name).applyMotionBlur(A.angle,A.distance);return 'ok';");
314
+ jsxTool("apply_add_noise", "Adds noise.", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applyAddNoise(A.amount,NoiseDistribution.GAUSSIAN,false);return 'ok';");
315
315
 
316
316
  /* ====================== AJUSTES ====================== */
317
- jsxTool("adjust_brightness_contrast", "Brillo/contraste de la capa.", { brightness: S.number().optional(), contrast: S.number().optional(), name: S.string().optional() }, "_act(A.name).adjustBrightnessContrast(A.brightness||0,A.contrast||0);return 'ok';");
318
- jsxTool("desaturate_layer", "Desatura la capa.", { name: S.string().optional() }, "_act(A.name).desaturate();return 'ok';");
319
- jsxTool("invert_layer", "Invierte colores de la capa.", { name: S.string().optional() }, "_act(A.name).invert();return 'ok';");
320
- jsxTool("posterize_layer", "Posteriza la capa.", { levels: S.number().optional(), name: S.string().optional() }, "_act(A.name).posterize(A.levels||4);return 'ok';");
321
- jsxTool("auto_levels", "Niveles automáticos.", { name: S.string().optional() }, "_act(A.name).autoLevels();return 'ok';");
317
+ jsxTool("adjust_brightness_contrast", "Layer brightness/contrast.", { brightness: S.number().optional(), contrast: S.number().optional(), name: S.string().optional() }, "_act(A.name).adjustBrightnessContrast(A.brightness||0,A.contrast||0);return 'ok';");
318
+ jsxTool("desaturate_layer", "Desaturates the layer.", { name: S.string().optional() }, "_act(A.name).desaturate();return 'ok';");
319
+ jsxTool("invert_layer", "Inverts the layer's colors.", { name: S.string().optional() }, "_act(A.name).invert();return 'ok';");
320
+ jsxTool("posterize_layer", "Posterizes the layer.", { levels: S.number().optional(), name: S.string().optional() }, "_act(A.name).posterize(A.levels||4);return 'ok';");
321
+ jsxTool("auto_levels", "Auto levels.", { name: S.string().optional() }, "_act(A.name).autoLevels();return 'ok';");
322
322
 
323
323
  /* ====================== EFECTOS DE CAPA ====================== */
324
- jsxTool("add_drop_shadow", "Agrega Sombra paralela.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), angle: S.number().optional(), distance: S.number().optional(), size: S.number().optional() },
325
- "var L=A.name?_need(A.name):_doc().activeLayer;_sel(L);var c=_rgb(A.color||'#000000');var ref=new ActionReference();ref.putProperty(charIDToTypeID('Prpr'),charIDToTypeID('Lefx'));ref.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var dd=new ActionDescriptor();dd.putReference(charIDToTypeID('null'),ref);var fx=new ActionDescriptor();fx.putUnitDouble(charIDToTypeID('Scl '),charIDToTypeID('#Prc'),100);var ds=new ActionDescriptor();ds.putBoolean(charIDToTypeID('enab'),true);ds.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Mltp'));var col=new ActionDescriptor();col.putDouble(charIDToTypeID('Rd '),c.r);col.putDouble(charIDToTypeID('Grn '),c.g);col.putDouble(charIDToTypeID('Bl '),c.b);ds.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),col);ds.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||75);ds.putUnitDouble(charIDToTypeID('lagl'),charIDToTypeID('#Ang'),A.angle||120);ds.putUnitDouble(charIDToTypeID('Dstn'),charIDToTypeID('#Pxl'),A.distance||5);ds.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||5);fx.putObject(charIDToTypeID('DrSh'),charIDToTypeID('DrSh'),ds);dd.putObject(charIDToTypeID('T '),charIDToTypeID('Lefx'),fx);executeAction(charIDToTypeID('setd'),dd,DialogModes.NO);return 'sombra agregada';");
326
- jsxTool("clear_layer_effects", "Quita los estilos de capa.", { name: S.string().optional() },
324
+ jsxTool("add_drop_shadow", "Adds a drop shadow.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), angle: S.number().optional(), distance: S.number().optional(), size: S.number().optional() },
325
+ "var L=A.name?_need(A.name):_doc().activeLayer;_sel(L);var c=_rgb(A.color||'#000000');var ref=new ActionReference();ref.putProperty(charIDToTypeID('Prpr'),charIDToTypeID('Lefx'));ref.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var dd=new ActionDescriptor();dd.putReference(charIDToTypeID('null'),ref);var fx=new ActionDescriptor();fx.putUnitDouble(charIDToTypeID('Scl '),charIDToTypeID('#Prc'),100);var ds=new ActionDescriptor();ds.putBoolean(charIDToTypeID('enab'),true);ds.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Mltp'));var col=new ActionDescriptor();col.putDouble(charIDToTypeID('Rd '),c.r);col.putDouble(charIDToTypeID('Grn '),c.g);col.putDouble(charIDToTypeID('Bl '),c.b);ds.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),col);ds.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||75);ds.putUnitDouble(charIDToTypeID('lagl'),charIDToTypeID('#Ang'),A.angle||120);ds.putUnitDouble(charIDToTypeID('Dstn'),charIDToTypeID('#Pxl'),A.distance||5);ds.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||5);fx.putObject(charIDToTypeID('DrSh'),charIDToTypeID('DrSh'),ds);dd.putObject(charIDToTypeID('T '),charIDToTypeID('Lefx'),fx);executeAction(charIDToTypeID('setd'),dd,DialogModes.NO);return 'shadow added';");
326
+ jsxTool("clear_layer_effects", "Removes the layer styles.", { name: S.string().optional() },
327
327
  "var L=A.name?_need(A.name):_doc().activeLayer;_sel(L);var ref=new ActionReference();ref.putProperty(charIDToTypeID('Prpr'),charIDToTypeID('Lefx'));ref.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var d=new ActionDescriptor();d.putReference(charIDToTypeID('null'),ref);executeAction(charIDToTypeID('dlfx'),d,DialogModes.NO);return 'ok';");
328
328
 
329
329
  /* ====================== SMART OBJECTS ====================== */
330
- jsxTool("replace_smart_object", "Reemplaza el contenido de un Smart Object con un archivo.", { name: S.string(), path: S.string() },
331
- "_sel(_need(A.name));var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));d.putInteger(charIDToTypeID('PgNm'),1);executeAction(stringIDToTypeID('placedLayerReplaceContents'),d,DialogModes.NO);return 'reemplazado';");
332
- jsxTool("convert_to_smart_object", "Convierte la capa activa (o nombrada) en Smart Object.", { name: S.string().optional() },
333
- "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('newPlacedLayer'),undefined,DialogModes.NO);return 'convertido';");
330
+ jsxTool("replace_smart_object", "Replaces a Smart Object's contents with a file.", { name: S.string(), path: S.string() },
331
+ "_sel(_need(A.name));var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));d.putInteger(charIDToTypeID('PgNm'),1);executeAction(stringIDToTypeID('placedLayerReplaceContents'),d,DialogModes.NO);return 'replaced';");
332
+ jsxTool("convert_to_smart_object", "Converts the active (or named) layer into a Smart Object.", { name: S.string().optional() },
333
+ "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('newPlacedLayer'),undefined,DialogModes.NO);return 'converted';");
334
334
 
335
335
  /* ====================== GUÍAS ====================== */
336
- jsxTool("add_guide", "Agrega una guía (direction: horizontal | vertical, position px).", { direction: S.string(), position: S.number() },
337
- "_doc().guides.add(A.direction==='vertical'?Direction.VERTICAL:Direction.HORIZONTAL,UnitValue(A.position,'px'));return 'guía';");
338
- jsxTool("clear_guides", "Elimina todas las guías.", {}, "executeAction(stringIDToTypeID('clearAllGuides'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
336
+ jsxTool("add_guide", "Adds a guide (direction: horizontal | vertical, position px).", { direction: S.string(), position: S.number() },
337
+ "_doc().guides.add(A.direction==='vertical'?Direction.VERTICAL:Direction.HORIZONTAL,UnitValue(A.position,'px'));return 'guide';");
338
+ jsxTool("clear_guides", "Deletes all guides.", {}, "executeAction(stringIDToTypeID('clearAllGuides'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
339
339
 
340
340
  /* ====================== ACCIONES ====================== */
341
- jsxTool("play_action", "Reproduce una acción grabada (.atn).", { set: S.string(), action: S.string() }, "app.doAction(A.action,A.set);return 'ok';");
341
+ jsxTool("play_action", "Plays a recorded action (.atn).", { set: S.string(), action: S.string() }, "app.doAction(A.action,A.set);return 'ok';");
342
342
 
343
343
  /* ====================== EXPORT ====================== */
344
- jsxTool("export_png", "Exporta el documento aplanado a PNG. Devuelve la ruta.", { path: S.string().optional() },
344
+ jsxTool("export_png", "Exports the flattened document to PNG. Returns the path.", { path: S.string().optional() },
345
345
  "var d=_doc();var dup=d.duplicate();dup.flatten();var p=(A.path&&A.path.length)?A.path:(Folder.temp.fsName+'/psmcp_doc_'+(new Date().getTime())+'.png');var f=new File(p);var o=new PNGSaveOptions();o.compression=6;o.interlaced=false;dup.saveAs(f,o,true,Extension.LOWERCASE);dup.close(SaveOptions.DONOTSAVECHANGES);return {path:f.fsName};");
346
- jsxTool("export_jpg", "Exporta el documento a JPG.", { path: S.string(), quality: S.number().optional() },
346
+ jsxTool("export_jpg", "Exports the document to JPG.", { path: S.string(), quality: S.number().optional() },
347
347
  "var o=new JPEGSaveOptions();o.quality=A.quality||10;_doc().saveAs(new File(A.path),o,true,Extension.LOWERCASE);return {path:A.path};");
348
- jsxTool("export_layer_png", "Exporta una capa/carpeta aislada a PNG (trim opcional). Devuelve la ruta.", { name: S.string(), trim: S.boolean().optional(), path: S.string().optional() },
349
- "var work=_doc().duplicate();app.activeDocument=work;var L=_find(A.name);if(!L){work.close(SaveOptions.DONOTSAVECHANGES);throw 'No se encontró la capa: '+A.name;}function hideAll(ls){for(var i=0;i<ls.length;i++){try{ls[i].visible=false;}catch(e){}if(ls[i].typename==='LayerSet')hideAll(ls[i].layers);}}function show(n){try{n.visible=true;}catch(e){}if(n.typename==='LayerSet')for(var i=0;i<n.layers.length;i++)show(n.layers[i]);}hideAll(work.layers);show(L);var p=L.parent;while(p&&p.typename==='LayerSet'){try{p.visible=true;}catch(e){}p=p.parent;}if(A.trim!==false){try{work.trim(TrimType.TRANSPARENT,true,true,true,true);}catch(e){}}var pth=(A.path&&A.path.length)?A.path:(Folder.temp.fsName+'/psmcp_layer_'+(new Date().getTime())+'.png');var f=new File(pth);var o=new PNGSaveOptions();o.compression=6;o.interlaced=false;work.saveAs(f,o,true,Extension.LOWERCASE);work.close(SaveOptions.DONOTSAVECHANGES);return {path:f.fsName};", 180000);
348
+ jsxTool("export_layer_png", "Exports an isolated layer/folder to PNG (optional trim). Returns the path.", { name: S.string(), trim: S.boolean().optional(), path: S.string().optional() },
349
+ "var work=_doc().duplicate();app.activeDocument=work;var L=_find(A.name);if(!L){work.close(SaveOptions.DONOTSAVECHANGES);throw 'Layer not found: '+A.name;}function hideAll(ls){for(var i=0;i<ls.length;i++){try{ls[i].visible=false;}catch(e){}if(ls[i].typename==='LayerSet')hideAll(ls[i].layers);}}function show(n){try{n.visible=true;}catch(e){}if(n.typename==='LayerSet')for(var i=0;i<n.layers.length;i++)show(n.layers[i]);}hideAll(work.layers);show(L);var p=L.parent;while(p&&p.typename==='LayerSet'){try{p.visible=true;}catch(e){}p=p.parent;}if(A.trim!==false){try{work.trim(TrimType.TRANSPARENT,true,true,true,true);}catch(e){}}var pth=(A.path&&A.path.length)?A.path:(Folder.temp.fsName+'/psmcp_layer_'+(new Date().getTime())+'.png');var f=new File(pth);var o=new PNGSaveOptions();o.compression=6;o.interlaced=false;work.saveAs(f,o,true,Extension.LOWERCASE);work.close(SaveOptions.DONOTSAVECHANGES);return {path:f.fsName};", 180000);
350
350
 
351
351
  /* ====================== DOCUMENTO (modo/profundidad/perfil) ====================== */
352
- jsxTool("change_color_mode", "Cambia el modo de color (RGB|CMYK|GRAYSCALE|LAB|BITMAP|INDEXEDCOLOR|MULTICHANNEL).", { mode: S.string() }, "_doc().changeMode(ChangeMode[A.mode]);return 'modo: '+A.mode;");
353
- jsxTool("change_bit_depth", "Bits por canal (8|16|32).", { bits: S.number() }, "_doc().bitsPerChannel=A.bits==32?BitsPerChannelType.THIRTYTWO:(A.bits==16?BitsPerChannelType.SIXTEEN:BitsPerChannelType.EIGHT);return 'ok';");
354
- jsxTool("reveal_all", "Expande el lienzo para mostrar todo el contenido.", {}, "_doc().revealAll();return 'ok';");
355
- jsxTool("get_active_layer", "Nombre de la capa activa.", {}, "return {name:_doc().activeLayer.name};");
352
+ jsxTool("change_color_mode", "Changes the color mode (RGB|CMYK|GRAYSCALE|LAB|BITMAP|INDEXEDCOLOR|MULTICHANNEL).", { mode: S.string() }, "_doc().changeMode(ChangeMode[A.mode]);return 'mode: '+A.mode;");
353
+ jsxTool("change_bit_depth", "Bits per channel (8|16|32).", { bits: S.number() }, "_doc().bitsPerChannel=A.bits==32?BitsPerChannelType.THIRTYTWO:(A.bits==16?BitsPerChannelType.SIXTEEN:BitsPerChannelType.EIGHT);return 'ok';");
354
+ jsxTool("reveal_all", "Expands the canvas to show all content.", {}, "_doc().revealAll();return 'ok';");
355
+ jsxTool("get_active_layer", "Name of the active layer.", {}, "return {name:_doc().activeLayer.name};");
356
356
 
357
357
  /* ====================== CAPAS (avanzado) ====================== */
358
- jsxTool("get_layer_bounds", "Bounds de una capa [x1,y1,x2,y2].", { name: S.string() }, "var b=_need(A.name).bounds;return [Math.round(b[0]),Math.round(b[1]),Math.round(b[2]),Math.round(b[3])];");
359
- jsxTool("set_layer_fill_opacity", "Opacidad de relleno (0-100).", { name: S.string(), fillOpacity: S.number() }, "_need(A.name).fillOpacity=A.fillOpacity;return 'ok';");
360
- jsxTool("lock_layer", "Bloquea/desbloquea una capa.", { name: S.string(), locked: S.boolean() }, "_need(A.name).allLocked=!!A.locked;return 'ok';");
361
- jsxTool("set_clipping_mask", "Máscara de recorte con la capa de abajo.", { name: S.string(), clip: S.boolean() }, "_need(A.name).grouped=!!A.clip;return 'ok';");
362
- jsxTool("clear_layer_pixels", "Borra los píxeles de una capa.", { name: S.string() }, "_sel(_need(A.name));var d=_doc();d.selection.selectAll();d.selection.clear();d.selection.deselect();return 'ok';");
363
- jsxTool("move_layer_to_front", "Mueve la capa al frente.", { name: S.string() }, "_need(A.name).move(_doc(),ElementPlacement.PLACEATBEGINNING);return 'ok';");
364
- jsxTool("move_layer_to_back", "Mueve la capa al fondo.", { name: S.string() }, "_need(A.name).move(_doc(),ElementPlacement.PLACEATEND);return 'ok';");
365
- jsxTool("set_layer_label_color", "Color de etiqueta (red|orange|yellow|green|blue|violet|gray|none).", { name: S.string(), color: S.string() },
358
+ jsxTool("get_layer_bounds", "Bounds of a layer [x1,y1,x2,y2].", { name: S.string() }, "var b=_need(A.name).bounds;return [Math.round(b[0]),Math.round(b[1]),Math.round(b[2]),Math.round(b[3])];");
359
+ jsxTool("set_layer_fill_opacity", "Fill opacity (0-100).", { name: S.string(), fillOpacity: S.number() }, "_need(A.name).fillOpacity=A.fillOpacity;return 'ok';");
360
+ jsxTool("lock_layer", "Locks/unlocks a layer.", { name: S.string(), locked: S.boolean() }, "_need(A.name).allLocked=!!A.locked;return 'ok';");
361
+ jsxTool("set_clipping_mask", "Clipping mask with the layer below.", { name: S.string(), clip: S.boolean() }, "_need(A.name).grouped=!!A.clip;return 'ok';");
362
+ jsxTool("clear_layer_pixels", "Clears a layer's pixels.", { name: S.string() }, "_sel(_need(A.name));var d=_doc();d.selection.selectAll();d.selection.clear();d.selection.deselect();return 'ok';");
363
+ jsxTool("move_layer_to_front", "Moves the layer to the front.", { name: S.string() }, "_need(A.name).move(_doc(),ElementPlacement.PLACEATBEGINNING);return 'ok';");
364
+ jsxTool("move_layer_to_back", "Moves the layer to the back.", { name: S.string() }, "_need(A.name).move(_doc(),ElementPlacement.PLACEATEND);return 'ok';");
365
+ jsxTool("set_layer_label_color", "Label color (red|orange|yellow|green|blue|violet|gray|none).", { name: S.string(), color: S.string() },
366
366
  "_sel(_need(A.name));var m={red:'Rd ',orange:'Orng',yellow:'Ylw ',green:'Grn ',blue:'Bl ',violet:'Vlt ',gray:'Gry ',none:'None'};var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);var d2=new ActionDescriptor();d2.putEnumerated(charIDToTypeID('Clr '),charIDToTypeID('Clr '),charIDToTypeID(m[A.color]||'None'));d.putObject(charIDToTypeID('T '),charIDToTypeID('Lyr '),d2);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok';");
367
367
 
368
368
  /* ====================== TEXTO (avanzado) ====================== */
369
- jsxTool("set_text_justification", "Justificación (LEFT|CENTER|RIGHT|FULLYJUSTIFIED).", { name: S.string(), justification: S.string() }, "_need(A.name).textItem.justification=Justification[A.justification];return 'ok';");
370
- jsxTool("set_text_tracking", "Tracking del texto.", { name: S.string(), tracking: S.number() }, "_need(A.name).textItem.tracking=A.tracking;return 'ok';");
371
- jsxTool("set_text_leading", "Interlineado (px).", { name: S.string(), leading: S.number() }, "var t=_need(A.name).textItem;t.useAutoLeading=false;t.leading=A.leading;return 'ok';");
372
- jsxTool("set_text_antialias", "Antialias (NONE|SHARP|CRISP|STRONG|SMOOTH).", { name: S.string(), method: S.string() }, "_need(A.name).textItem.antiAliasMethod=AntiAlias[A.method];return 'ok';");
373
- jsxTool("set_text_box", "Convierte a texto de párrafo con tamaño (px).", { name: S.string(), width: S.number(), height: S.number() }, "var t=_need(A.name).textItem;t.kind=TextType.PARAGRAPHTEXT;t.width=UnitValue(A.width,'px');t.height=UnitValue(A.height,'px');return 'ok';");
369
+ jsxTool("set_text_justification", "Justification (LEFT|CENTER|RIGHT|FULLYJUSTIFIED).", { name: S.string(), justification: S.string() }, "_need(A.name).textItem.justification=Justification[A.justification];return 'ok';");
370
+ jsxTool("set_text_tracking", "Text tracking.", { name: S.string(), tracking: S.number() }, "_need(A.name).textItem.tracking=A.tracking;return 'ok';");
371
+ jsxTool("set_text_leading", "Leading (px).", { name: S.string(), leading: S.number() }, "var t=_need(A.name).textItem;t.useAutoLeading=false;t.leading=A.leading;return 'ok';");
372
+ jsxTool("set_text_antialias", "Antialiasing (NONE|SHARP|CRISP|STRONG|SMOOTH).", { name: S.string(), method: S.string() }, "_need(A.name).textItem.antiAliasMethod=AntiAlias[A.method];return 'ok';");
373
+ jsxTool("set_text_box", "Converts to paragraph text with size (px).", { name: S.string(), width: S.number(), height: S.number() }, "var t=_need(A.name).textItem;t.kind=TextType.PARAGRAPHTEXT;t.width=UnitValue(A.width,'px');t.height=UnitValue(A.height,'px');return 'ok';");
374
374
 
375
375
  /* ====================== SELECCIÓN (avanzado) ====================== */
376
- jsxTool("select_ellipse", "Selección elíptica (bounding box px).", { x: S.number(), y: S.number(), width: S.number(), height: S.number() },
376
+ jsxTool("select_ellipse", "Elliptical selection (bounding box px).", { x: S.number(), y: S.number(), width: S.number(), height: S.number() },
377
377
  "var d=new ActionDescriptor();var r=new ActionReference();r.putProperty(charIDToTypeID('Chnl'),charIDToTypeID('fsel'));d.putReference(charIDToTypeID('null'),r);var e=new ActionDescriptor();e.putUnitDouble(charIDToTypeID('Top '),charIDToTypeID('#Pxl'),A.y);e.putUnitDouble(charIDToTypeID('Left'),charIDToTypeID('#Pxl'),A.x);e.putUnitDouble(charIDToTypeID('Btom'),charIDToTypeID('#Pxl'),A.y+A.height);e.putUnitDouble(charIDToTypeID('Rght'),charIDToTypeID('#Pxl'),A.x+A.width);d.putObject(charIDToTypeID('T '),charIDToTypeID('Elps'),e);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok';");
378
- jsxTool("contract_selection", "Contrae la selección (px).", { pixels: S.number() }, "_doc().selection.contract(A.pixels);return 'ok';");
379
- jsxTool("border_selection", "Convierte la selección en borde (px).", { width: S.number() }, "_doc().selection.border(A.width);return 'ok';");
380
- jsxTool("smooth_selection", "Suaviza la selección (px).", { radius: S.number() }, "_doc().selection.smooth(A.radius);return 'ok';");
381
- jsxTool("copy_selection", "Copia la selección (merged=true para combinado).", { merged: S.boolean().optional() }, "_doc().selection.copy(A.merged||false);return 'ok';");
382
- jsxTool("cut_selection", "Corta la selección.", {}, "_doc().selection.cut();return 'ok';");
383
- jsxTool("clear_selection_pixels", "Borra los píxeles de la selección.", {}, "_doc().selection.clear();return 'ok';");
384
- jsxTool("paste", "Pega el portapapeles.", {}, "app.activeDocument.paste();return 'ok';");
385
- jsxTool("make_work_path", "Crea una ruta de trabajo desde la selección.", { tolerance: S.number().optional() }, "_doc().selection.makeWorkPath(A.tolerance||2);return 'ok';");
386
- jsxTool("transform_selection_move", "Mueve la selección (dx,dy px).", { dx: S.number(), dy: S.number() }, "_doc().selection.translate(A.dx,A.dy);return 'ok';");
387
- jsxTool("save_selection_to_channel", "Guarda la selección como canal alfa.", { name: S.string().optional() },
388
- "var ch=_doc().channels.add();if(A.name)ch.name=A.name;_doc().selection.store(ch);return 'guardada: '+ch.name;");
378
+ jsxTool("contract_selection", "Contracts the selection (px).", { pixels: S.number() }, "_doc().selection.contract(A.pixels);return 'ok';");
379
+ jsxTool("border_selection", "Converts the selection into a border (px).", { width: S.number() }, "_doc().selection.border(A.width);return 'ok';");
380
+ jsxTool("smooth_selection", "Smooths the selection (px).", { radius: S.number() }, "_doc().selection.smooth(A.radius);return 'ok';");
381
+ jsxTool("copy_selection", "Copies the selection (merged=true for merged copy).", { merged: S.boolean().optional() }, "_doc().selection.copy(A.merged||false);return 'ok';");
382
+ jsxTool("cut_selection", "Cuts the selection.", {}, "_doc().selection.cut();return 'ok';");
383
+ jsxTool("clear_selection_pixels", "Clears the selection's pixels.", {}, "_doc().selection.clear();return 'ok';");
384
+ jsxTool("paste", "Pastes the clipboard.", {}, "app.activeDocument.paste();return 'ok';");
385
+ jsxTool("make_work_path", "Creates a work path from the selection.", { tolerance: S.number().optional() }, "_doc().selection.makeWorkPath(A.tolerance||2);return 'ok';");
386
+ jsxTool("transform_selection_move", "Moves the selection (dx,dy px).", { dx: S.number(), dy: S.number() }, "_doc().selection.translate(A.dx,A.dy);return 'ok';");
387
+ jsxTool("save_selection_to_channel", "Saves the selection as an alpha channel.", { name: S.string().optional() },
388
+ "var ch=_doc().channels.add();if(A.name)ch.name=A.name;_doc().selection.store(ch);return 'saved: '+ch.name;");
389
389
 
390
390
  /* ====================== CANALES ====================== */
391
- jsxTool("list_channels", "Lista los canales.", {}, "var a=[];var ch=_doc().channels;for(var i=0;i<ch.length;i++)a.push(ch[i].name);return a;");
392
- jsxTool("add_alpha_channel", "Agrega un canal alfa.", { name: S.string().optional() }, "var c=_doc().channels.add();if(A.name)c.name=A.name;return 'canal: '+c.name;");
393
- jsxTool("delete_channel", "Elimina un canal por nombre.", { name: S.string() }, "_doc().channels.getByName(A.name).remove();return 'ok';");
394
- jsxTool("duplicate_channel", "Duplica un canal por nombre.", { name: S.string() }, "_doc().channels.getByName(A.name).duplicate();return 'ok';");
391
+ jsxTool("list_channels", "Lists the channels.", {}, "var a=[];var ch=_doc().channels;for(var i=0;i<ch.length;i++)a.push(ch[i].name);return a;");
392
+ jsxTool("add_alpha_channel", "Adds an alpha channel.", { name: S.string().optional() }, "var c=_doc().channels.add();if(A.name)c.name=A.name;return 'channel: '+c.name;");
393
+ jsxTool("delete_channel", "Deletes a channel by name.", { name: S.string() }, "_doc().channels.getByName(A.name).remove();return 'ok';");
394
+ jsxTool("duplicate_channel", "Duplicates a channel by name.", { name: S.string() }, "_doc().channels.getByName(A.name).duplicate();return 'ok';");
395
395
 
396
396
  /* ====================== MÁSCARAS (más) ====================== */
397
- jsxTool("delete_layer_mask", "Elimina la máscara de la capa activa/nombrada.", { name: S.string().optional() },
397
+ jsxTool("delete_layer_mask", "Deletes the active/named layer's mask.", { name: S.string().optional() },
398
398
  "if(A.name)_sel(_need(A.name));var r=new ActionReference();r.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var d=new ActionDescriptor();d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Dlt '),d,DialogModes.NO);return 'ok';");
399
399
 
400
400
  /* ====================== TRANSFORM (más) ====================== */
401
- jsxTool("skew_layer", "Sesga una capa (matriz no; usa flip/rotate/scale). Placeholder vía rotate.", { name: S.string(), angle: S.number() }, "_need(A.name).rotate(A.angle,AnchorPosition.MIDDLECENTER);return 'ok';");
402
- jsxTool("set_layer_position", "Mueve una capa a una posición absoluta (esquina sup-izq px).", { name: S.string(), x: S.number(), y: S.number() }, "var L=_need(A.name);var b=L.bounds;L.translate(A.x-b[0].as('px'),A.y-b[1].as('px'));return 'ok';");
401
+ jsxTool("skew_layer", "Skews a layer (no matrix; uses flip/rotate/scale). Placeholder via rotate.", { name: S.string(), angle: S.number() }, "_need(A.name).rotate(A.angle,AnchorPosition.MIDDLECENTER);return 'ok';");
402
+ jsxTool("set_layer_position", "Moves a layer to an absolute position (top-left corner px).", { name: S.string(), x: S.number(), y: S.number() }, "var L=_need(A.name);var b=L.bounds;L.translate(A.x-b[0].as('px'),A.y-b[1].as('px'));return 'ok';");
403
403
 
404
404
  /* ====================== FILTROS (exhaustivo, DOM) ====================== */
405
- jsxTool("apply_blur", "Desenfoque.", { name: S.string().optional() }, "_act(A.name).applyBlur();return 'ok';");
406
- jsxTool("apply_blur_more", "Desenfoque ++.", { name: S.string().optional() }, "_act(A.name).applyBlurMore();return 'ok';");
407
- jsxTool("apply_sharpen_more", "Enfocar ++.", { name: S.string().optional() }, "_act(A.name).applySharpenMore();return 'ok';");
408
- jsxTool("apply_sharpen_edges", "Enfocar bordes.", { name: S.string().optional() }, "_act(A.name).applySharpenEdges();return 'ok';");
409
- jsxTool("apply_despeckle", "Destramar.", { name: S.string().optional() }, "_act(A.name).applyDespeckle();return 'ok';");
410
- jsxTool("apply_clouds", "Nubes (usa color frontal/fondo).", { name: S.string().optional() }, "_act(A.name).applyClouds();return 'ok';");
411
- jsxTool("apply_difference_clouds", "Nubes de diferencia.", { name: S.string().optional() }, "_act(A.name).applyDifferenceClouds();return 'ok';");
412
- jsxTool("apply_high_pass", "Paso alto (radio px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyHighPass(A.radius);return 'ok';");
413
- jsxTool("apply_median", "Mediana (radio px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyMedianNoise(A.radius);return 'ok';");
414
- jsxTool("apply_maximum", "Máximo (radio px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyMaximum(A.radius);return 'ok';");
415
- jsxTool("apply_minimum", "Mínimo (radio px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyMinimum(A.radius);return 'ok';");
416
- jsxTool("apply_offset", "Desplazamiento (h,v px, wrap).", { horizontal: S.number(), vertical: S.number(), name: S.string().optional() }, "_act(A.name).applyOffset(A.horizontal,A.vertical,OffsetUndefinedAreas.WRAPAROUND);return 'ok';");
417
- jsxTool("apply_pinch", "Encoger (-100..100).", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applyPinch(A.amount);return 'ok';");
418
- jsxTool("apply_polar_coordinates", "Coordenadas polares (toPolar=true).", { toPolar: S.boolean().optional(), name: S.string().optional() }, "_act(A.name).applyPolarCoordinates(A.toPolar===false?PolarConversionType.POLARTORECTANGULAR:PolarConversionType.RECTANGULARTOPOLAR);return 'ok';");
419
- jsxTool("apply_radial_blur", "Desenfoque radial (amount).", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applyRadialBlur(A.amount,RadialBlurMethod.SPIN,RadialBlurQuality.GOOD);return 'ok';");
420
- jsxTool("apply_ripple", "Onda.", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applyRipple(A.amount,RippleSize.MEDIUM);return 'ok';");
421
- jsxTool("apply_smart_blur", "Desenfoque inteligente.", { radius: S.number(), threshold: S.number(), name: S.string().optional() }, "_act(A.name).applySmartBlur(A.radius,A.threshold,SmartBlurQuality.HIGH,SmartBlurMode.NORMAL);return 'ok';");
422
- jsxTool("apply_solarize", "Solarizar.", { name: S.string().optional() }, "_act(A.name).applySolarize();return 'ok';");
423
- jsxTool("apply_spherize", "Esferizar (-100..100).", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applySpherize(A.amount,SpherizeMode.NORMAL);return 'ok';");
424
- jsxTool("apply_twirl", "Molinete (ángulo).", { angle: S.number(), name: S.string().optional() }, "_act(A.name).applyTwirl(A.angle);return 'ok';");
405
+ jsxTool("apply_blur", "Blur.", { name: S.string().optional() }, "_act(A.name).applyBlur();return 'ok';");
406
+ jsxTool("apply_blur_more", "Blur more.", { name: S.string().optional() }, "_act(A.name).applyBlurMore();return 'ok';");
407
+ jsxTool("apply_sharpen_more", "Sharpen more.", { name: S.string().optional() }, "_act(A.name).applySharpenMore();return 'ok';");
408
+ jsxTool("apply_sharpen_edges", "Sharpen edges.", { name: S.string().optional() }, "_act(A.name).applySharpenEdges();return 'ok';");
409
+ jsxTool("apply_despeckle", "Despeckle.", { name: S.string().optional() }, "_act(A.name).applyDespeckle();return 'ok';");
410
+ jsxTool("apply_clouds", "Clouds (uses foreground/background color).", { name: S.string().optional() }, "_act(A.name).applyClouds();return 'ok';");
411
+ jsxTool("apply_difference_clouds", "Difference clouds.", { name: S.string().optional() }, "_act(A.name).applyDifferenceClouds();return 'ok';");
412
+ jsxTool("apply_high_pass", "High pass (radius px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyHighPass(A.radius);return 'ok';");
413
+ jsxTool("apply_median", "Median (radius px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyMedianNoise(A.radius);return 'ok';");
414
+ jsxTool("apply_maximum", "Maximum (radius px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyMaximum(A.radius);return 'ok';");
415
+ jsxTool("apply_minimum", "Minimum (radius px).", { radius: S.number(), name: S.string().optional() }, "_act(A.name).applyMinimum(A.radius);return 'ok';");
416
+ jsxTool("apply_offset", "Offset (h,v px, wrap).", { horizontal: S.number(), vertical: S.number(), name: S.string().optional() }, "_act(A.name).applyOffset(A.horizontal,A.vertical,OffsetUndefinedAreas.WRAPAROUND);return 'ok';");
417
+ jsxTool("apply_pinch", "Pinch (-100..100).", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applyPinch(A.amount);return 'ok';");
418
+ jsxTool("apply_polar_coordinates", "Polar coordinates (toPolar=true).", { toPolar: S.boolean().optional(), name: S.string().optional() }, "_act(A.name).applyPolarCoordinates(A.toPolar===false?PolarConversionType.POLARTORECTANGULAR:PolarConversionType.RECTANGULARTOPOLAR);return 'ok';");
419
+ jsxTool("apply_radial_blur", "Radial blur (amount).", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applyRadialBlur(A.amount,RadialBlurMethod.SPIN,RadialBlurQuality.GOOD);return 'ok';");
420
+ jsxTool("apply_ripple", "Ripple.", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applyRipple(A.amount,RippleSize.MEDIUM);return 'ok';");
421
+ jsxTool("apply_smart_blur", "Smart blur.", { radius: S.number(), threshold: S.number(), name: S.string().optional() }, "_act(A.name).applySmartBlur(A.radius,A.threshold,SmartBlurQuality.HIGH,SmartBlurMode.NORMAL);return 'ok';");
422
+ jsxTool("apply_solarize", "Solarize.", { name: S.string().optional() }, "_act(A.name).applySolarize();return 'ok';");
423
+ jsxTool("apply_spherize", "Spherize (-100..100).", { amount: S.number(), name: S.string().optional() }, "_act(A.name).applySpherize(A.amount,SpherizeMode.NORMAL);return 'ok';");
424
+ jsxTool("apply_twirl", "Twirl (angle).", { angle: S.number(), name: S.string().optional() }, "_act(A.name).applyTwirl(A.angle);return 'ok';");
425
425
  jsxTool("apply_zigzag", "Zigzag.", { amount: S.number(), ridges: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyZigZag(A.amount,A.ridges||5,ZigZagType.AROUNDCENTER);return 'ok';");
426
- jsxTool("apply_wind", "Viento (left|right).", { direction: S.string().optional(), name: S.string().optional() }, "_act(A.name).applyWind(WindMethod.WIND,A.direction==='left'?WindDirection.LEFT:WindDirection.RIGHT);return 'ok';");
427
- jsxTool("apply_dust_and_scratches", "Polvo y rascaduras.", { radius: S.number(), threshold: S.number(), name: S.string().optional() }, "_act(A.name).applyDustAndScratches(A.radius,A.threshold);return 'ok';");
428
- jsxTool("apply_diffuse_glow", "Resplandor difuso.", { graininess: S.number().optional(), glow: S.number().optional(), clear: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyDiffuseGlow(A.graininess||6,A.glow||10,A.clear||15);return 'ok';");
429
- jsxTool("apply_ocean_ripple", "Onda marina.", { size: S.number().optional(), magnitude: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyOceanRipple(A.size||9,A.magnitude||9);return 'ok';");
430
- jsxTool("apply_lens_flare", "Destello (brillo, x, y).", { brightness: S.number().optional(), x: S.number().optional(), y: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyLensFlare(A.brightness||100,[A.x||0,A.y||0],LensType.ZOOM50300MM);return 'ok';");
426
+ jsxTool("apply_wind", "Wind (left|right).", { direction: S.string().optional(), name: S.string().optional() }, "_act(A.name).applyWind(WindMethod.WIND,A.direction==='left'?WindDirection.LEFT:WindDirection.RIGHT);return 'ok';");
427
+ jsxTool("apply_dust_and_scratches", "Dust & scratches.", { radius: S.number(), threshold: S.number(), name: S.string().optional() }, "_act(A.name).applyDustAndScratches(A.radius,A.threshold);return 'ok';");
428
+ jsxTool("apply_diffuse_glow", "Diffuse glow.", { graininess: S.number().optional(), glow: S.number().optional(), clear: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyDiffuseGlow(A.graininess||6,A.glow||10,A.clear||15);return 'ok';");
429
+ jsxTool("apply_ocean_ripple", "Ocean ripple.", { size: S.number().optional(), magnitude: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyOceanRipple(A.size||9,A.magnitude||9);return 'ok';");
430
+ jsxTool("apply_lens_flare", "Lens flare (brightness, x, y).", { brightness: S.number().optional(), x: S.number().optional(), y: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyLensFlare(A.brightness||100,[A.x||0,A.y||0],LensType.ZOOM50300MM);return 'ok';");
431
431
 
432
432
  /* ====================== AJUSTES (destructivos, DOM) ====================== */
433
- jsxTool("adjust_levels", "Niveles.", { inBlack: S.number().optional(), inWhite: S.number().optional(), gamma: S.number().optional(), outBlack: S.number().optional(), outWhite: S.number().optional(), name: S.string().optional() }, "_act(A.name).adjustLevels(A.inBlack||0,A.inWhite||255,A.gamma||1,A.outBlack||0,A.outWhite||255);return 'ok';");
434
- jsxTool("adjust_color_balance", "Balance de color (cian-rojo, magenta-verde, amarillo-azul).", { cyanRed: S.number().optional(), magentaGreen: S.number().optional(), yellowBlue: S.number().optional(), name: S.string().optional() }, "var a=[A.cyanRed||0,A.magentaGreen||0,A.yellowBlue||0];_act(A.name).adjustColorBalance(a,a,a,true);return 'ok';");
435
- jsxTool("equalize_layer", "Ecualizar.", { name: S.string().optional() }, "_act(A.name).equalize();return 'ok';");
436
- jsxTool("threshold_layer", "Umbral.", { level: S.number().optional(), name: S.string().optional() }, "_act(A.name).threshold(A.level||128);return 'ok';");
437
- jsxTool("auto_contrast", "Contraste automático.", { name: S.string().optional() }, "_act(A.name).autoContrast();return 'ok';");
433
+ jsxTool("adjust_levels", "Levels.", { inBlack: S.number().optional(), inWhite: S.number().optional(), gamma: S.number().optional(), outBlack: S.number().optional(), outWhite: S.number().optional(), name: S.string().optional() }, "_act(A.name).adjustLevels(A.inBlack||0,A.inWhite||255,A.gamma||1,A.outBlack||0,A.outWhite||255);return 'ok';");
434
+ jsxTool("adjust_color_balance", "Color balance (cyan-red, magenta-green, yellow-blue).", { cyanRed: S.number().optional(), magentaGreen: S.number().optional(), yellowBlue: S.number().optional(), name: S.string().optional() }, "var a=[A.cyanRed||0,A.magentaGreen||0,A.yellowBlue||0];_act(A.name).adjustColorBalance(a,a,a,true);return 'ok';");
435
+ jsxTool("equalize_layer", "Equalize.", { name: S.string().optional() }, "_act(A.name).equalize();return 'ok';");
436
+ jsxTool("threshold_layer", "Threshold.", { level: S.number().optional(), name: S.string().optional() }, "_act(A.name).threshold(A.level||128);return 'ok';");
437
+ jsxTool("auto_contrast", "Auto contrast.", { name: S.string().optional() }, "_act(A.name).autoContrast();return 'ok';");
438
438
 
439
439
  /* ====================== CAPAS DE AJUSTE (no destructivas, AM, best-effort) ====================== */
440
- jsxTool("create_adjustment_layer", "Crea una capa de ajuste (type: brightness|levels|curves|hueSaturation|colorBalance|blackWhite|photoFilter|channelMixer|invert|posterize|threshold|selectiveColor|vibrance|exposure|gradientMap).", { type: S.string() },
441
- "var map={brightness:'brightnessEvent',levels:'levels',curves:'curves',hueSaturation:'hueSaturation',colorBalance:'colorBalance',blackWhite:'blackAndWhite',photoFilter:'photoFilter',channelMixer:'channelMixer',invert:'invert',posterize:'posterization',threshold:'thresholdClassEvent',selectiveColor:'selectiveColor',vibrance:'vibrance',exposure:'exposure',gradientMap:'gradientMapClass'};var t=map[A.type];if(!t)throw 'tipo desconocido: '+A.type;var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('adjustmentLayer'));d.putReference(charIDToTypeID('null'),r);var lyr=new ActionDescriptor();var adj=new ActionDescriptor();lyr.putObject(charIDToTypeID('Type'),stringIDToTypeID(t),adj);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('adjustmentLayer'),lyr);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'capa de ajuste: '+A.type;");
440
+ jsxTool("create_adjustment_layer", "Creates an adjustment layer (type: brightness|levels|curves|hueSaturation|colorBalance|blackWhite|photoFilter|channelMixer|invert|posterize|threshold|selectiveColor|vibrance|exposure|gradientMap).", { type: S.string() },
441
+ "var map={brightness:'brightnessEvent',levels:'levels',curves:'curves',hueSaturation:'hueSaturation',colorBalance:'colorBalance',blackWhite:'blackAndWhite',photoFilter:'photoFilter',channelMixer:'channelMixer',invert:'invert',posterize:'posterization',threshold:'thresholdClassEvent',selectiveColor:'selectiveColor',vibrance:'vibrance',exposure:'exposure',gradientMap:'gradientMapClass'};var t=map[A.type];if(!t)throw 'unknown type: '+A.type;var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('adjustmentLayer'));d.putReference(charIDToTypeID('null'),r);var lyr=new ActionDescriptor();var adj=new ActionDescriptor();lyr.putObject(charIDToTypeID('Type'),stringIDToTypeID(t),adj);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('adjustmentLayer'),lyr);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'adjustment layer: '+A.type;");
442
442
 
443
443
  /* ====================== ESTILOS DE CAPA (AM, best-effort; reemplazan el estilo existente) ====================== */
444
- jsxTool("add_stroke_effect", "Trazo (estilo de capa). Reemplaza el estilo existente.", { name: S.string().optional(), color: S.string().optional(), size: S.number().optional(), position: S.string().optional(), opacity: S.number().optional() },
445
- "var L=A.name?_need(A.name):_doc().activeLayer;_sel(L);var c=_rgb(A.color||'#000000');var ref=new ActionReference();ref.putProperty(charIDToTypeID('Prpr'),charIDToTypeID('Lefx'));ref.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var dd=new ActionDescriptor();dd.putReference(charIDToTypeID('null'),ref);var fx=new ActionDescriptor();fx.putUnitDouble(charIDToTypeID('Scl '),charIDToTypeID('#Prc'),100);var fr=new ActionDescriptor();fr.putBoolean(charIDToTypeID('enab'),true);fr.putEnumerated(charIDToTypeID('Styl'),charIDToTypeID('FStl'),A.position==='inside'?charIDToTypeID('InsF'):(A.position==='center'?charIDToTypeID('CtrF'):charIDToTypeID('OutF')));fr.putEnumerated(charIDToTypeID('PntT'),charIDToTypeID('FrFl'),charIDToTypeID('SClr'));fr.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Nrml'));fr.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||100);fr.putUnitDouble(charIDToTypeID('Sz '),charIDToTypeID('#Pxl'),A.size||3);var col=new ActionDescriptor();col.putDouble(charIDToTypeID('Rd '),c.r);col.putDouble(charIDToTypeID('Grn '),c.g);col.putDouble(charIDToTypeID('Bl '),c.b);fr.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),col);fx.putObject(charIDToTypeID('FrFX'),charIDToTypeID('FrFX'),fr);dd.putObject(charIDToTypeID('T '),charIDToTypeID('Lefx'),fx);executeAction(charIDToTypeID('setd'),dd,DialogModes.NO);return 'trazo';");
446
- jsxTool("add_color_overlay", "Superposición de color (estilo de capa). Reemplaza el estilo existente.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional() },
444
+ jsxTool("add_stroke_effect", "Stroke (layer style). Replaces the existing style.", { name: S.string().optional(), color: S.string().optional(), size: S.number().optional(), position: S.string().optional(), opacity: S.number().optional() },
445
+ "var L=A.name?_need(A.name):_doc().activeLayer;_sel(L);var c=_rgb(A.color||'#000000');var ref=new ActionReference();ref.putProperty(charIDToTypeID('Prpr'),charIDToTypeID('Lefx'));ref.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var dd=new ActionDescriptor();dd.putReference(charIDToTypeID('null'),ref);var fx=new ActionDescriptor();fx.putUnitDouble(charIDToTypeID('Scl '),charIDToTypeID('#Prc'),100);var fr=new ActionDescriptor();fr.putBoolean(charIDToTypeID('enab'),true);fr.putEnumerated(charIDToTypeID('Styl'),charIDToTypeID('FStl'),A.position==='inside'?charIDToTypeID('InsF'):(A.position==='center'?charIDToTypeID('CtrF'):charIDToTypeID('OutF')));fr.putEnumerated(charIDToTypeID('PntT'),charIDToTypeID('FrFl'),charIDToTypeID('SClr'));fr.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Nrml'));fr.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||100);fr.putUnitDouble(charIDToTypeID('Sz '),charIDToTypeID('#Pxl'),A.size||3);var col=new ActionDescriptor();col.putDouble(charIDToTypeID('Rd '),c.r);col.putDouble(charIDToTypeID('Grn '),c.g);col.putDouble(charIDToTypeID('Bl '),c.b);fr.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),col);fx.putObject(charIDToTypeID('FrFX'),charIDToTypeID('FrFX'),fr);dd.putObject(charIDToTypeID('T '),charIDToTypeID('Lefx'),fx);executeAction(charIDToTypeID('setd'),dd,DialogModes.NO);return 'stroke';");
446
+ jsxTool("add_color_overlay", "Color overlay (layer style). Replaces the existing style.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional() },
447
447
  "var L=A.name?_need(A.name):_doc().activeLayer;_sel(L);var c=_rgb(A.color||'#FF0000');var ref=new ActionReference();ref.putProperty(charIDToTypeID('Prpr'),charIDToTypeID('Lefx'));ref.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var dd=new ActionDescriptor();dd.putReference(charIDToTypeID('null'),ref);var fx=new ActionDescriptor();fx.putUnitDouble(charIDToTypeID('Scl '),charIDToTypeID('#Prc'),100);var so=new ActionDescriptor();so.putBoolean(charIDToTypeID('enab'),true);so.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Nrml'));var col=new ActionDescriptor();col.putDouble(charIDToTypeID('Rd '),c.r);col.putDouble(charIDToTypeID('Grn '),c.g);col.putDouble(charIDToTypeID('Bl '),c.b);so.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),col);so.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||100);fx.putObject(charIDToTypeID('SoFi'),charIDToTypeID('SoFi'),so);dd.putObject(charIDToTypeID('T '),charIDToTypeID('Lefx'),fx);executeAction(charIDToTypeID('setd'),dd,DialogModes.NO);return 'overlay';");
448
448
 
449
449
  /* ====================== SMART OBJECTS (más) ====================== */
450
- jsxTool("rasterize_smart_object", "Rasteriza un Smart Object.", { name: S.string() }, "_need(A.name).rasterize(RasterizeType.ENTIRELAYER);return 'ok';");
450
+ jsxTool("rasterize_smart_object", "Rasterizes a Smart Object.", { name: S.string() }, "_need(A.name).rasterize(RasterizeType.ENTIRELAYER);return 'ok';");
451
451
 
452
452
  /* ====================== COLOR / MUESTREO ====================== */
453
- jsxTool("get_pixel_color", "Color del pixel en (x,y) → HEX.", { x: S.number(), y: S.number() }, "var s=_doc().colorSamplers.add([UnitValue(A.x,'px'),UnitValue(A.y,'px')]);var c='#'+s.color.rgb.hexValue;s.remove();return {color:c.toUpperCase()};");
454
- jsxTool("set_background_color", "Color de fondo.", { color: S.string() }, "app.backgroundColor=_hex(A.color);return 'ok';");
455
- jsxTool("swap_colors", "Intercambia color frontal y de fondo.", {}, "var f=app.foregroundColor;app.foregroundColor=app.backgroundColor;app.backgroundColor=f;return 'ok';");
453
+ jsxTool("get_pixel_color", "Pixel color at (x,y) → HEX.", { x: S.number(), y: S.number() }, "var s=_doc().colorSamplers.add([UnitValue(A.x,'px'),UnitValue(A.y,'px')]);var c='#'+s.color.rgb.hexValue;s.remove();return {color:c.toUpperCase()};");
454
+ jsxTool("set_background_color", "Background color.", { color: S.string() }, "app.backgroundColor=_hex(A.color);return 'ok';");
455
+ jsxTool("swap_colors", "Swaps foreground and background colors.", {}, "var f=app.foregroundColor;app.foregroundColor=app.backgroundColor;app.backgroundColor=f;return 'ok';");
456
456
 
457
457
  /* ====================== HISTORIA ====================== */
458
- jsxTool("undo", "Deshace el último paso.", {}, "executeAction(charIDToTypeID('undo'),undefined,DialogModes.NO);return 'ok';");
459
- jsxTool("create_snapshot", "Crea una instantánea de historia.", { name: S.string().optional() }, "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('snapshotClass'));d.putReference(stringIDToTypeID('null'),r);var f=new ActionReference();f.putProperty(stringIDToTypeID('historyState'),stringIDToTypeID('currentHistoryState'));d.putReference(stringIDToTypeID('from'),f);if(A.name)d.putString(stringIDToTypeID('name'),A.name);executeAction(stringIDToTypeID('make'),d,DialogModes.NO);return 'snapshot';");
458
+ jsxTool("undo", "Undoes the last step.", {}, "executeAction(charIDToTypeID('undo'),undefined,DialogModes.NO);return 'ok';");
459
+ jsxTool("create_snapshot", "Creates a history snapshot.", { name: S.string().optional() }, "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('snapshotClass'));d.putReference(stringIDToTypeID('null'),r);var f=new ActionReference();f.putProperty(stringIDToTypeID('historyState'),stringIDToTypeID('currentHistoryState'));d.putReference(stringIDToTypeID('from'),f);if(A.name)d.putString(stringIDToTypeID('name'),A.name);executeAction(stringIDToTypeID('make'),d,DialogModes.NO);return 'snapshot';");
460
460
 
461
461
  /* ====================== GUÍAS (más) ====================== */
462
- jsxTool("list_guides", "Lista las guías.", {}, "var a=[];var g=_doc().guides;for(var i=0;i<g.length;i++)a.push({direction:String(g[i].direction).replace('Direction.',''),coordinate:Math.round(g[i].coordinate)});return a;");
462
+ jsxTool("list_guides", "Lists the guides.", {}, "var a=[];var g=_doc().guides;for(var i=0;i<g.length;i++)a.push({direction:String(g[i].direction).replace('Direction.',''),coordinate:Math.round(g[i].coordinate)});return a;");
463
463
 
464
464
  /* ====================== EXPORT (más) ====================== */
465
- jsxTool("save_for_web_png", "Exporta PNG optimizado (Save for Web).", { path: S.string() }, "var o=new ExportOptionsSaveForWeb();o.format=SaveDocumentType.PNG;o.PNG8=false;o.transparency=true;_doc().exportDocument(new File(A.path),ExportType.SAVEFORWEB,o);return {path:A.path};");
466
- jsxTool("export_tiff", "Exporta a TIFF (LZW).", { path: S.string() }, "var o=new TiffSaveOptions();o.imageCompression=TIFFEncoding.TIFFLZW;_doc().saveAs(new File(A.path),o,true,Extension.LOWERCASE);return {path:A.path};");
465
+ jsxTool("save_for_web_png", "Exports optimized PNG (Save for Web).", { path: S.string() }, "var o=new ExportOptionsSaveForWeb();o.format=SaveDocumentType.PNG;o.PNG8=false;o.transparency=true;_doc().exportDocument(new File(A.path),ExportType.SAVEFORWEB,o);return {path:A.path};");
466
+ jsxTool("export_tiff", "Exports to TIFF (LZW).", { path: S.string() }, "var o=new TiffSaveOptions();o.imageCompression=TIFFEncoding.TIFFLZW;_doc().saveAs(new File(A.path),o,true,Extension.LOWERCASE);return {path:A.path};");
467
467
 
468
468
  /* ====================== DOCUMENTO / METADATA / HISTORIA (más) ====================== */
469
- jsxTool("get_document_metadata", "Metadata del documento (autor, título, copyright, keywords, etc.).", {},
469
+ jsxTool("get_document_metadata", "Document metadata (author, title, copyright, keywords, etc.).", {},
470
470
  "var i=_doc().info;function g(f){try{return i[f];}catch(e){return null;}}return {author:g('author'),title:g('title'),caption:g('caption'),copyrightNotice:g('copyrightNotice'),copyrighted:g('copyrighted'),credit:g('credit'),city:g('city'),provinceState:g('provinceState'),country:g('country'),keywords:g('keywords'),captionWriter:g('captionWriter'),jobName:g('jobName'),instructions:g('instructions')};");
471
- jsxTool("set_document_metadata", "Setea metadata del documento (cualquier campo de document.info).", { author: S.string().optional(), title: S.string().optional(), caption: S.string().optional(), copyrightNotice: S.string().optional(), credit: S.string().optional(), city: S.string().optional(), country: S.string().optional(), keywords: S.array(S.string()).optional() },
471
+ jsxTool("set_document_metadata", "Sets document metadata (any field of document.info).", { author: S.string().optional(), title: S.string().optional(), caption: S.string().optional(), copyrightNotice: S.string().optional(), credit: S.string().optional(), city: S.string().optional(), country: S.string().optional(), keywords: S.array(S.string()).optional() },
472
472
  "var i=_doc().info;if(A.author!=null)i.author=A.author;if(A.title!=null)i.title=A.title;if(A.caption!=null)i.caption=A.caption;if(A.copyrightNotice!=null)i.copyrightNotice=A.copyrightNotice;if(A.credit!=null)i.credit=A.credit;if(A.city!=null)i.city=A.city;if(A.country!=null)i.country=A.country;if(A.keywords!=null)i.keywords=A.keywords;return 'ok';");
473
- jsxTool("get_xmp_metadata", "XMP crudo del documento (string).", {}, "return {xmp:String(_doc().xmpMetadata.rawData)};");
474
- jsxTool("get_histogram", "Histograma del documento (256 valores del canal compuesto luminosidad).", {}, "return _doc().histogram;");
475
- jsxTool("resize_image_resample", "Redimensiona la imagen eligiendo método de remuestreo (NONE|BICUBIC|BICUBICSHARPER|BICUBICSMOOTHER|BILINEAR|NEARESTNEIGHBOR|PRESERVEDETAILS|AUTOMATIC).", { width: S.number().optional(), height: S.number().optional(), resolution: S.number().optional(), method: S.string().optional() },
476
- "var d=_doc();d.resizeImage(A.width?UnitValue(A.width,'px'):undefined,A.height?UnitValue(A.height,'px'):undefined,A.resolution||undefined,A.method?ResampleMethod[A.method]:ResampleMethod.BICUBICAUTOMATIC);return 'imagen '+Math.round(d.width)+'x'+Math.round(d.height);");
477
- jsxTool("list_history_states", "Lista los estados de historia del documento.", {}, "var a=[];var h=_doc().historyStates;for(var i=0;i<h.length;i++)a.push({index:i,name:h[i].name,snapshot:h[i].snapshot});return a;");
478
- jsxTool("set_history_state", "Va a un estado de historia por índice.", { index: S.number() }, "_doc().activeHistoryState=_doc().historyStates[A.index];return 'ok';");
479
- jsxTool("redo", "Rehace el último paso.", {}, "executeAction(charIDToTypeID('redo'),undefined,DialogModes.NO);return 'ok';");
480
- jsxTool("step_backward", "Paso atrás en la historia.", {}, "executeAction(stringIDToTypeID('undo'),undefined,DialogModes.NO);return 'ok';");
481
- jsxTool("count_items", "Cuenta objetos de conteo (herramienta Recuento) del documento.", {}, "try{return {count:_doc().countItems.length};}catch(e){return {count:0};}");
482
- jsxTool("select_document_bounds", "Selecciona el rectángulo completo del lienzo.", {}, "_doc().selection.selectAll();return 'ok';");
473
+ jsxTool("get_xmp_metadata", "Raw document XMP (string).", {}, "return {xmp:String(_doc().xmpMetadata.rawData)};");
474
+ jsxTool("get_histogram", "Document histogram (256 values of the composite luminosity channel).", {}, "return _doc().histogram;");
475
+ jsxTool("resize_image_resample", "Resizes the image choosing a resample method (NONE|BICUBIC|BICUBICSHARPER|BICUBICSMOOTHER|BILINEAR|NEARESTNEIGHBOR|PRESERVEDETAILS|AUTOMATIC).", { width: S.number().optional(), height: S.number().optional(), resolution: S.number().optional(), method: S.string().optional() },
476
+ "var d=_doc();d.resizeImage(A.width?UnitValue(A.width,'px'):undefined,A.height?UnitValue(A.height,'px'):undefined,A.resolution||undefined,A.method?ResampleMethod[A.method]:ResampleMethod.BICUBICAUTOMATIC);return 'image '+Math.round(d.width)+'x'+Math.round(d.height);");
477
+ jsxTool("list_history_states", "Lists the document's history states.", {}, "var a=[];var h=_doc().historyStates;for(var i=0;i<h.length;i++)a.push({index:i,name:h[i].name,snapshot:h[i].snapshot});return a;");
478
+ jsxTool("set_history_state", "Goes to a history state by index.", { index: S.number() }, "_doc().activeHistoryState=_doc().historyStates[A.index];return 'ok';");
479
+ jsxTool("redo", "Redoes the last step.", {}, "executeAction(charIDToTypeID('redo'),undefined,DialogModes.NO);return 'ok';");
480
+ jsxTool("step_backward", "Steps back in history.", {}, "executeAction(stringIDToTypeID('undo'),undefined,DialogModes.NO);return 'ok';");
481
+ jsxTool("count_items", "Counts count-tool objects in the document.", {}, "try{return {count:_doc().countItems.length};}catch(e){return {count:0};}");
482
+ jsxTool("select_document_bounds", "Selects the entire canvas rectangle.", {}, "_doc().selection.selectAll();return 'ok';");
483
483
 
484
484
  /* ====================== EXPORT (formatos extra) ====================== */
485
- jsxTool("export_pdf", "Exporta a PDF (Photoshop PDF).", { path: S.string() },
485
+ jsxTool("export_pdf", "Exports to PDF (Photoshop PDF).", { path: S.string() },
486
486
  "var o=new PDFSaveOptions();o.PDFStandard=PDFStandard.NONE;o.encoding=PDFEncoding.JPEG;o.jpegQuality=10;_doc().saveAs(new File(A.path),o,true,Extension.LOWERCASE);return {path:A.path};");
487
- jsxTool("export_bmp", "Exporta a BMP.", { path: S.string() },
487
+ jsxTool("export_bmp", "Exports to BMP.", { path: S.string() },
488
488
  "var o=new BMPSaveOptions();o.depth=BMPDepthType.TWENTYFOUR;_doc().saveAs(new File(A.path),o,true,Extension.LOWERCASE);return {path:A.path};");
489
- jsxTool("export_targa", "Exporta a TGA.", { path: S.string() },
489
+ jsxTool("export_targa", "Exports to TGA.", { path: S.string() },
490
490
  "var o=new TargaSaveOptions();o.resolution=TargaBitsPerPixels.TWENTYFOUR;_doc().saveAs(new File(A.path),o,true,Extension.LOWERCASE);return {path:A.path};");
491
- jsxTool("export_gif", "Exporta GIF optimizado (Save for Web).", { path: S.string() },
491
+ jsxTool("export_gif", "Exports optimized GIF (Save for Web).", { path: S.string() },
492
492
  "var o=new ExportOptionsSaveForWeb();o.format=SaveDocumentType.COMPUSERVEGIF;o.colors=256;_doc().exportDocument(new File(A.path),ExportType.SAVEFORWEB,o);return {path:A.path};");
493
- jsxTool("export_jpg_sfw", "Exporta JPG optimizado (Save for Web).", { path: S.string(), quality: S.number().optional() },
493
+ jsxTool("export_jpg_sfw", "Exports optimized JPG (Save for Web).", { path: S.string(), quality: S.number().optional() },
494
494
  "var o=new ExportOptionsSaveForWeb();o.format=SaveDocumentType.JPEG;o.quality=A.quality||80;_doc().exportDocument(new File(A.path),ExportType.SAVEFORWEB,o);return {path:A.path};");
495
495
 
496
496
  /* ====================== APP / FUENTES / PREFERENCIAS ====================== */
497
- jsxTool("list_fonts", "Lista las fuentes instaladas (opcional filter por substring). Devuelve name/postScriptName/family/style.", { filter: S.string().optional(), limit: S.number().optional() },
497
+ jsxTool("list_fonts", "Lists installed fonts (optional filter by substring). Returns name/postScriptName/family/style.", { filter: S.string().optional(), limit: S.number().optional() },
498
498
  "var a=[];var f=app.fonts;var q=A.filter?String(A.filter).toLowerCase():null;var lim=A.limit||0;for(var i=0;i<f.length;i++){var it=f[i];var nm=String(it.name);if(q&&nm.toLowerCase().indexOf(q)<0&&String(it.family).toLowerCase().indexOf(q)<0)continue;a.push({name:nm,postScriptName:String(it.postScriptName),family:String(it.family),style:String(it.style)});if(lim&&a.length>=lim)break;}return a;");
499
- jsxTool("set_ruler_units", "Unidades de regla (PIXELS|INCHES|CM|MM|POINTS|PICAS|PERCENT).", { units: S.string() }, "app.preferences.rulerUnits=Units[A.units];return 'ok: '+A.units;");
500
- jsxTool("get_system_info", "Info del sistema/host (versión PS, OS, build).", {}, "return {version:app.version,build:(app.build!==undefined?String(app.build):''),os:String($.os),locale:String($.locale)};");
499
+ jsxTool("set_ruler_units", "Ruler units (PIXELS|INCHES|CM|MM|POINTS|PICAS|PERCENT).", { units: S.string() }, "app.preferences.rulerUnits=Units[A.units];return 'ok: '+A.units;");
500
+ jsxTool("get_system_info", "System/host info (PS version, OS, build).", {}, "return {version:app.version,build:(app.build!==undefined?String(app.build):''),os:String($.os),locale:String($.locale)};");
501
501
 
502
502
  /* ====================== CAPAS: SELECCIÓN MÚLTIPLE / GRUPOS / ORDEN / ALINEAR ====================== */
503
- jsxTool("select_layers", "Selecciona varias capas por nombre (selección múltiple real).", { names: S.array(S.string()) },
504
- "var names=A.names||[];if(!names.length)throw 'names vacío';for(var k=0;k<names.length;k++){var L=_need(names[k]);var d=new ActionDescriptor();var r=new ActionReference();r.putName(charIDToTypeID('Lyr '),names[k]);d.putReference(charIDToTypeID('null'),r);if(k>0)d.putEnumerated(stringIDToTypeID('selectionModifier'),stringIDToTypeID('selectionModifierType'),stringIDToTypeID('addToSelection'));d.putBoolean(charIDToTypeID('MkVs'),false);executeAction(charIDToTypeID('slct'),d,DialogModes.NO);}return 'seleccionadas: '+names.length;");
505
- jsxTool("select_all_layers", "Selecciona todas las capas del nivel superior.", {}, "var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(stringIDToTypeID('selectAllLayers'),d,DialogModes.NO);return 'ok';");
506
- jsxTool("deselect_layers", "Deselecciona todas las capas.", {}, "var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(stringIDToTypeID('selectNoLayers'),d,DialogModes.NO);return 'ok';");
507
- jsxTool("group_selected_layers", "Agrupa las capas seleccionadas en una carpeta.", { name: S.string().optional() },
508
- "var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(stringIDToTypeID('groupLayersEvent'),d,DialogModes.NO);if(A.name){try{_doc().activeLayer.name=A.name;}catch(e){}}return 'agrupadas';");
509
- jsxTool("ungroup_layers", "Desagrupa el grupo activo (o nombrado).", { name: S.string().optional() },
503
+ jsxTool("select_layers", "Selects several layers by name (true multi-selection).", { names: S.array(S.string()) },
504
+ "var names=A.names||[];if(!names.length)throw 'empty names';for(var k=0;k<names.length;k++){var L=_need(names[k]);var d=new ActionDescriptor();var r=new ActionReference();r.putName(charIDToTypeID('Lyr '),names[k]);d.putReference(charIDToTypeID('null'),r);if(k>0)d.putEnumerated(stringIDToTypeID('selectionModifier'),stringIDToTypeID('selectionModifierType'),stringIDToTypeID('addToSelection'));d.putBoolean(charIDToTypeID('MkVs'),false);executeAction(charIDToTypeID('slct'),d,DialogModes.NO);}return 'selected: '+names.length;");
505
+ jsxTool("select_all_layers", "Selects all top-level layers.", {}, "var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(stringIDToTypeID('selectAllLayers'),d,DialogModes.NO);return 'ok';");
506
+ jsxTool("deselect_layers", "Deselects all layers.", {}, "var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(stringIDToTypeID('selectNoLayers'),d,DialogModes.NO);return 'ok';");
507
+ jsxTool("group_selected_layers", "Groups the selected layers into a folder.", { name: S.string().optional() },
508
+ "var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(stringIDToTypeID('groupLayersEvent'),d,DialogModes.NO);if(A.name){try{_doc().activeLayer.name=A.name;}catch(e){}}return 'grouped';");
509
+ jsxTool("ungroup_layers", "Ungroups the active (or named) group.", { name: S.string().optional() },
510
510
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(stringIDToTypeID('ungroupLayersEvent'),d,DialogModes.NO);return 'ok';");
511
- jsxTool("reorder_layer", "Mueve una capa arriba/abajo de otra (position: above|below).", { name: S.string(), target: S.string(), position: S.string().optional() },
511
+ jsxTool("reorder_layer", "Moves a layer above/below another (position: above|below).", { name: S.string(), target: S.string(), position: S.string().optional() },
512
512
  "var L=_need(A.name);var t=_need(A.target);L.move(t,A.position==='below'?ElementPlacement.PLACEAFTER:ElementPlacement.PLACEBEFORE);return 'ok';");
513
- jsxTool("move_layer_into_group", "Mueve una capa dentro de un grupo.", { name: S.string(), group: S.string() },
514
- "var L=_need(A.name);var g=_need(A.group);if(g.typename!=='LayerSet')throw 'no es grupo: '+A.group;if(g.layers.length){L.move(g.layers[0],ElementPlacement.PLACEBEFORE);}else{L.move(g,ElementPlacement.INSIDE);}return 'ok';");
515
- jsxTool("link_selected_layers", "Vincula las capas seleccionadas.", {}, "executeAction(stringIDToTypeID('linkSelectedLayers'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
516
- jsxTool("unlink_layer", "Desvincula una capa.", { name: S.string() }, "_need(A.name).unlink();return 'ok';");
517
- jsxTool("align_layers", "Alinea las capas seleccionadas (edge: left|centerH|right|top|centerV|bottom). Requiere 2+ seleccionadas.", { edge: S.string() },
518
- "var m={left:'ADSLefts',centerH:'ADSCentersH',right:'ADSRights',top:'ADSTops',centerV:'ADSCentersV',bottom:'ADSBottoms'};var e=m[A.edge];if(!e)throw 'edge inválido';var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);d.putEnumerated(charIDToTypeID('Usng'),stringIDToTypeID('alignDistributeSelector'),stringIDToTypeID(e));executeAction(charIDToTypeID('Algn'),d,DialogModes.NO);return 'ok';");
519
- jsxTool("distribute_layers", "Distribuye las capas seleccionadas (mode: left|centerH|right|top|centerV|bottom). Requiere 3+.", { mode: S.string() },
520
- "var m={left:'ADSLefts',centerH:'ADSCentersH',right:'ADSRights',top:'ADSTops',centerV:'ADSCentersV',bottom:'ADSBottoms'};var e=m[A.mode];if(!e)throw 'mode inválido';var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);d.putEnumerated(charIDToTypeID('Usng'),stringIDToTypeID('alignDistributeSelector'),stringIDToTypeID(e));executeAction(stringIDToTypeID('distribute'),d,DialogModes.NO);return 'ok';");
521
- jsxTool("convert_background_to_layer", "Convierte la capa de fondo en capa normal.", { name: S.string().optional() },
513
+ jsxTool("move_layer_into_group", "Moves a layer inside a group.", { name: S.string(), group: S.string() },
514
+ "var L=_need(A.name);var g=_need(A.group);if(g.typename!=='LayerSet')throw 'not a group: '+A.group;if(g.layers.length){L.move(g.layers[0],ElementPlacement.PLACEBEFORE);}else{L.move(g,ElementPlacement.INSIDE);}return 'ok';");
515
+ jsxTool("link_selected_layers", "Links the selected layers.", {}, "executeAction(stringIDToTypeID('linkSelectedLayers'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
516
+ jsxTool("unlink_layer", "Unlinks a layer.", { name: S.string() }, "_need(A.name).unlink();return 'ok';");
517
+ jsxTool("align_layers", "Aligns the selected layers (edge: left|centerH|right|top|centerV|bottom). Requires 2+ selected.", { edge: S.string() },
518
+ "var m={left:'ADSLefts',centerH:'ADSCentersH',right:'ADSRights',top:'ADSTops',centerV:'ADSCentersV',bottom:'ADSBottoms'};var e=m[A.edge];if(!e)throw 'invalid edge';var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);d.putEnumerated(charIDToTypeID('Usng'),stringIDToTypeID('alignDistributeSelector'),stringIDToTypeID(e));executeAction(charIDToTypeID('Algn'),d,DialogModes.NO);return 'ok';");
519
+ jsxTool("distribute_layers", "Distributes the selected layers (mode: left|centerH|right|top|centerV|bottom). Requires 3+.", { mode: S.string() },
520
+ "var m={left:'ADSLefts',centerH:'ADSCentersH',right:'ADSRights',top:'ADSTops',centerV:'ADSCentersV',bottom:'ADSBottoms'};var e=m[A.mode];if(!e)throw 'invalid mode';var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);d.putEnumerated(charIDToTypeID('Usng'),stringIDToTypeID('alignDistributeSelector'),stringIDToTypeID(e));executeAction(stringIDToTypeID('distribute'),d,DialogModes.NO);return 'ok';");
521
+ jsxTool("convert_background_to_layer", "Converts the background layer into a normal layer.", { name: S.string().optional() },
522
522
  "var L=A.name?_need(A.name):_doc().activeLayer;if(L.isBackgroundLayer){L.isBackgroundLayer=false;}return 'ok: '+L.name;");
523
- jsxTool("get_layer_id", "ID interno de una capa (útil para trazabilidad).", { name: S.string() },
523
+ jsxTool("get_layer_id", "A layer's internal ID (useful for traceability).", { name: S.string() },
524
524
  "_sel(_need(A.name));var r=new ActionReference();r.putProperty(charIDToTypeID('Prpr'),charIDToTypeID('LyrI'));r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));return {id:executeActionGet(r).getInteger(charIDToTypeID('LyrI'))};");
525
525
 
526
526
  /* ====================== TEXTO (más) ====================== */
527
- jsxTool("set_text_baseline_shift", "Desplazamiento de línea base (px).", { name: S.string(), shift: S.number() }, "_need(A.name).textItem.baselineShift=A.shift;return 'ok';");
528
- jsxTool("set_text_faux_bold", "Negrita falsa on/off.", { name: S.string(), on: S.boolean() }, "_need(A.name).textItem.fauxBold=!!A.on;return 'ok';");
529
- jsxTool("set_text_faux_italic", "Itálica falsa on/off.", { name: S.string(), on: S.boolean() }, "_need(A.name).textItem.fauxItalic=!!A.on;return 'ok';");
530
- jsxTool("set_text_underline", "Subrayado (UNDERLINEOFF|UNDERLINELEFT|UNDERLINERIGHT).", { name: S.string(), style: S.string().optional() }, "_need(A.name).textItem.underline=UnderlineType[A.style||'UNDERLINELEFT'];return 'ok';");
531
- jsxTool("set_text_all_caps", "Mayúsculas (ALLCAPS|SMALLCAPS|NORMAL).", { name: S.string(), style: S.string().optional() }, "_need(A.name).textItem.capitalization=Case[A.style||'ALLCAPS'];return 'ok';");
532
- jsxTool("set_text_hyphenation", "Guionado on/off (texto de párrafo).", { name: S.string(), on: S.boolean() }, "_need(A.name).textItem.hyphenation=!!A.on;return 'ok';");
533
- jsxTool("set_text_autoleading", "Interlineado automático on/off (y porcentaje).", { name: S.string(), on: S.boolean(), percent: S.number().optional() }, "var t=_need(A.name).textItem;t.useAutoLeading=!!A.on;if(A.on&&A.percent)t.autoLeadingAmount=A.percent;return 'ok';");
534
- jsxTool("warp_text", "Aplica warp al texto (style: arc|arcLower|arcUpper|arch|bulge|shellLower|shellUpper|flag|wave|fish|rise|fisheye|inflate|squeeze|twist).", { name: S.string(), style: S.string(), bend: S.number().optional(), horizontal: S.number().optional(), vertical: S.number().optional() },
527
+ jsxTool("set_text_baseline_shift", "Baseline shift (px).", { name: S.string(), shift: S.number() }, "_need(A.name).textItem.baselineShift=A.shift;return 'ok';");
528
+ jsxTool("set_text_faux_bold", "Faux bold on/off.", { name: S.string(), on: S.boolean() }, "_need(A.name).textItem.fauxBold=!!A.on;return 'ok';");
529
+ jsxTool("set_text_faux_italic", "Faux italic on/off.", { name: S.string(), on: S.boolean() }, "_need(A.name).textItem.fauxItalic=!!A.on;return 'ok';");
530
+ jsxTool("set_text_underline", "Underline (UNDERLINEOFF|UNDERLINELEFT|UNDERLINERIGHT).", { name: S.string(), style: S.string().optional() }, "_need(A.name).textItem.underline=UnderlineType[A.style||'UNDERLINELEFT'];return 'ok';");
531
+ jsxTool("set_text_all_caps", "Capitalization (ALLCAPS|SMALLCAPS|NORMAL).", { name: S.string(), style: S.string().optional() }, "_need(A.name).textItem.capitalization=Case[A.style||'ALLCAPS'];return 'ok';");
532
+ jsxTool("set_text_hyphenation", "Hyphenation on/off (paragraph text).", { name: S.string(), on: S.boolean() }, "_need(A.name).textItem.hyphenation=!!A.on;return 'ok';");
533
+ jsxTool("set_text_autoleading", "Auto leading on/off (and percentage).", { name: S.string(), on: S.boolean(), percent: S.number().optional() }, "var t=_need(A.name).textItem;t.useAutoLeading=!!A.on;if(A.on&&A.percent)t.autoLeadingAmount=A.percent;return 'ok';");
534
+ jsxTool("warp_text", "Applies warp to the text (style: arc|arcLower|arcUpper|arch|bulge|shellLower|shellUpper|flag|wave|fish|rise|fisheye|inflate|squeeze|twist).", { name: S.string(), style: S.string(), bend: S.number().optional(), horizontal: S.number().optional(), vertical: S.number().optional() },
535
535
  "var t=_need(A.name).textItem;var m={arc:WarpStyle.ARC,arcLower:WarpStyle.ARCLOWER,arcUpper:WarpStyle.ARCUPPER,arch:WarpStyle.ARCH,bulge:WarpStyle.BULGE,shellLower:WarpStyle.SHELLLOWER,shellUpper:WarpStyle.SHELLUPPER,flag:WarpStyle.FLAG,wave:WarpStyle.WAVE,fish:WarpStyle.FISH,rise:WarpStyle.RISE,fisheye:WarpStyle.FISHEYE,inflate:WarpStyle.INFLATE,squeeze:WarpStyle.SQUEEZE,twist:WarpStyle.TWIST};t.warpStyle=m[A.style]||WarpStyle.ARC;t.warpBend=(A.bend!=null?A.bend:50)/100;t.warpHorizontalDistortion=(A.horizontal||0)/100;t.warpVerticalDistortion=(A.vertical||0)/100;return 'ok';");
536
- jsxTool("set_font_by_postscript", "Setea la fuente por su PostScript name (más confiable que el display name).", { name: S.string(), postScriptName: S.string() }, "_need(A.name).textItem.font=A.postScriptName;return 'ok';");
536
+ jsxTool("set_font_by_postscript", "Sets the font by its PostScript name (more reliable than the display name).", { name: S.string(), postScriptName: S.string() }, "_need(A.name).textItem.font=A.postScriptName;return 'ok';");
537
537
 
538
538
  /* ====================== SELECCIÓN (avanzado 2) ====================== */
539
- jsxTool("select_subject", "Selecciona el sujeto automáticamente (IA).", {}, "executeAction(stringIDToTypeID('autoCutout'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
540
- jsxTool("select_sky", "Selecciona el cielo automáticamente.", {}, "var d=new ActionDescriptor();var r=new ActionReference();r.putProperty(charIDToTypeID('Chnl'),charIDToTypeID('fsel'));d.putReference(charIDToTypeID('null'),r);d.putEnumerated(stringIDToTypeID('sampleAllLayers'),stringIDToTypeID('sampleAllLayers'),stringIDToTypeID('sampleAllLayers'));executeAction(stringIDToTypeID('selectSky'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
541
- jsxTool("select_color_range", "Selección por gama de colores alrededor de un pixel (x,y) con tolerancia (fuzziness 0-200).", { x: S.number(), y: S.number(), fuzziness: S.number().optional() },
539
+ jsxTool("select_subject", "Automatically selects the subject (AI).", {}, "executeAction(stringIDToTypeID('autoCutout'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
540
+ jsxTool("select_sky", "Automatically selects the sky.", {}, "var d=new ActionDescriptor();var r=new ActionReference();r.putProperty(charIDToTypeID('Chnl'),charIDToTypeID('fsel'));d.putReference(charIDToTypeID('null'),r);d.putEnumerated(stringIDToTypeID('sampleAllLayers'),stringIDToTypeID('sampleAllLayers'),stringIDToTypeID('sampleAllLayers'));executeAction(stringIDToTypeID('selectSky'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
541
+ jsxTool("select_color_range", "Color-range selection around a pixel (x,y) with tolerance (fuzziness 0-200).", { x: S.number(), y: S.number(), fuzziness: S.number().optional() },
542
542
  "var d=_doc();var s=d.colorSamplers.add([UnitValue(A.x,'px'),UnitValue(A.y,'px')]);var col=s.color;s.remove();d.selection.selectAll();return d.selection.deselect(),(function(){var desc=new ActionDescriptor();desc.putInteger(stringIDToTypeID('fuzziness'),A.fuzziness||40);var c=new ActionDescriptor();c.putDouble(charIDToTypeID('Rd '),col.rgb.red);c.putDouble(charIDToTypeID('Grn '),col.rgb.green);c.putDouble(charIDToTypeID('Bl '),col.rgb.blue);desc.putObject(charIDToTypeID('Mnm '),charIDToTypeID('RGBC'),c);desc.putObject(charIDToTypeID('Mxm '),charIDToTypeID('RGBC'),c);executeAction(stringIDToTypeID('colorRange'),desc,DialogModes.NO);return 'ok';})();");
543
- jsxTool("grow_selection", "[NO VERIFICADO — el ID de comando 'Grow' es correcto (confirmado por Adobe docs) pero sigue fallando ('comando no disponible') incluso en documento fresco; la estructura del descriptor debe ser distinta a la de similar_selection, no encontrada] Extiende la selección a píxeles adyacentes similares (grow). Hacelo manualmente (Seleccionar > Modificar > Ampliar) si esto falla.", { tolerance: S.number().optional() },
543
+ jsxTool("grow_selection", "[UNVERIFIED — the 'Grow' command ID is correct (confirmed by Adobe docs) but it still fails ('command unavailable') even on a fresh document; the descriptor structure must differ from similar_selection's, not found] Extends the selection to similar adjacent pixels (grow). Do it manually (Select > Modify > Grow) if this fails.", { tolerance: S.number().optional() },
544
544
  "var d=new ActionDescriptor();d.putInteger(charIDToTypeID('Tlrn'),A.tolerance||32);d.putBoolean(charIDToTypeID('AntA'),true);executeAction(charIDToTypeID('Grow'),d,DialogModes.NO);return 'ok';");
545
- jsxTool("similar_selection", "[NO VERIFICADO — mismo problema de estructura que grow_selection, confirmado con el mismo descriptor] Selecciona píxeles similares en todo el documento (similar). Hacelo manualmente (Seleccionar > Similares) si esto falla.", { tolerance: S.number().optional() },
545
+ jsxTool("similar_selection", "[UNVERIFIED — same structural issue as grow_selection, confirmed with the same descriptor] Selects similar pixels across the whole document (similar). Do it manually (Select > Similar) if this fails.", { tolerance: S.number().optional() },
546
546
  "var d=new ActionDescriptor();d.putInteger(charIDToTypeID('Tlrn'),A.tolerance||32);d.putBoolean(charIDToTypeID('AntA'),true);executeAction(charIDToTypeID('Smlr'),d,DialogModes.NO);return 'ok';");
547
- jsxTool("load_channel_as_selection", "Carga un canal alfa como selección por nombre.", { name: S.string() },
547
+ jsxTool("load_channel_as_selection", "Loads an alpha channel as a selection by name.", { name: S.string() },
548
548
  "var ch=_doc().channels.getByName(A.name);_doc().selection.load(ch);return 'ok';");
549
- jsxTool("select_polygon", "Selección poligonal por lista de puntos [[x,y],...].", { points: S.array(S.array(S.number())) },
550
- "var pts=A.points||[];if(pts.length<3)throw 'mínimo 3 puntos';var arr=[];for(var i=0;i<pts.length;i++)arr.push([pts[i][0],pts[i][1]]);_doc().selection.select(arr);return 'ok';");
551
- jsxTool("magic_wand_select", "Varita mágica: selecciona por color en (x,y) con tolerancia.", { x: S.number(), y: S.number(), tolerance: S.number().optional() },
549
+ jsxTool("select_polygon", "Polygonal selection from a list of points [[x,y],...].", { points: S.array(S.array(S.number())) },
550
+ "var pts=A.points||[];if(pts.length<3)throw 'minimum 3 points';var arr=[];for(var i=0;i<pts.length;i++)arr.push([pts[i][0],pts[i][1]]);_doc().selection.select(arr);return 'ok';");
551
+ jsxTool("magic_wand_select", "Magic wand: selects by color at (x,y) with tolerance.", { x: S.number(), y: S.number(), tolerance: S.number().optional() },
552
552
  "var d=new ActionDescriptor();var r=new ActionReference();r.putProperty(charIDToTypeID('Chnl'),charIDToTypeID('fsel'));d.putReference(charIDToTypeID('null'),r);var p=new ActionDescriptor();p.putUnitDouble(charIDToTypeID('Hrzn'),charIDToTypeID('#Pxl'),A.x);p.putUnitDouble(charIDToTypeID('Vrtc'),charIDToTypeID('#Pxl'),A.y);d.putObject(charIDToTypeID('T '),charIDToTypeID('Pnt '),p);d.putInteger(charIDToTypeID('Tlrn'),A.tolerance||32);d.putBoolean(charIDToTypeID('AntA'),true);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok';");
553
- jsxTool("get_selection_bounds", "Bounds de la selección actual [x1,y1,x2,y2] o null.", {},
553
+ jsxTool("get_selection_bounds", "Bounds of the current selection [x1,y1,x2,y2] or null.", {},
554
554
  "try{var b=_doc().selection.bounds;return [Math.round(b[0]),Math.round(b[1]),Math.round(b[2]),Math.round(b[3])];}catch(e){return null;}");
555
555
 
556
556
  /* ====================== MÁSCARAS (avanzado) ====================== */
557
- jsxTool("apply_layer_mask", "Aplica (fusiona) la máscara de capa a los píxeles.", { name: S.string().optional() },
558
- "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);d.putBoolean(charIDToTypeID('Aply'),true);executeAction(charIDToTypeID('Dlt '),d,DialogModes.NO);return 'aplicada';");
559
- jsxTool("toggle_layer_mask", "Habilita/deshabilita la máscara de capa.", { name: S.string().optional(), enabled: S.boolean() },
557
+ jsxTool("apply_layer_mask", "Applies (merges) the layer mask into the pixels.", { name: S.string().optional() },
558
+ "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);d.putBoolean(charIDToTypeID('Aply'),true);executeAction(charIDToTypeID('Dlt '),d,DialogModes.NO);return 'applied';");
559
+ jsxTool("toggle_layer_mask", "Enables/disables the layer mask.", { name: S.string().optional(), enabled: S.boolean() },
560
560
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);var m=new ActionDescriptor();m.putBoolean(charIDToTypeID('UsrM'),!!A.enabled);d.putObject(charIDToTypeID('T '),charIDToTypeID('Lyr '),m);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok';");
561
- jsxTool("invert_layer_mask", "Invierte la máscara de la capa activa/nombrada.", { name: S.string().optional() },
561
+ jsxTool("invert_layer_mask", "Inverts the active/named layer's mask.", { name: S.string().optional() },
562
562
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('slct'),d,DialogModes.NO);executeAction(charIDToTypeID('Invr'),undefined,DialogModes.NO);var r2=new ActionReference();r2.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var d2=new ActionDescriptor();d2.putReference(charIDToTypeID('null'),r2);return 'ok';");
563
- jsxTool("mask_from_selection", "Crea máscara de capa a partir de la selección actual (revela la selección).", { name: S.string().optional() },
563
+ jsxTool("mask_from_selection", "Creates a layer mask from the current selection (reveals the selection).", { name: S.string().optional() },
564
564
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putClass(charIDToTypeID('Nw '),charIDToTypeID('Chnl'));var r=new ActionReference();r.putEnumerated(charIDToTypeID('Chnl'),charIDToTypeID('Chnl'),charIDToTypeID('Msk '));d.putReference(charIDToTypeID('At '),r);d.putEnumerated(charIDToTypeID('Usng'),charIDToTypeID('UsrM'),charIDToTypeID('RvlS'));executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'ok';");
565
- jsxTool("add_vector_mask", "Agrega máscara vectorial desde la ruta de trabajo actual.", { name: S.string().optional() },
565
+ jsxTool("add_vector_mask", "Adds a vector mask from the current work path.", { name: S.string().optional() },
566
566
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();var r=new ActionReference();r.putClass(charIDToTypeID('Path'));d.putReference(charIDToTypeID('null'),r);var r2=new ActionReference();r2.putEnumerated(charIDToTypeID('Path'),charIDToTypeID('Path'),charIDToTypeID('vectorMask'));d.putReference(charIDToTypeID('At '),r2);var r3=new ActionReference();r3.putClass(charIDToTypeID('Path'));d.putReference(charIDToTypeID('Usng'),r3);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'ok';");
567
567
 
568
568
  /* ====================== CANALES (más) ====================== */
569
- jsxTool("split_channels", "Separa los canales en documentos independientes.", {}, "var docs=_doc().splitChannels();var a=[];for(var i=0;i<docs.length;i++)a.push(docs[i].name);return a;");
570
- jsxTool("set_active_channels", "Activa canales por nombre (ej. ['Red','Green']).", { names: S.array(S.string()) },
569
+ jsxTool("split_channels", "Splits the channels into separate documents.", {}, "var docs=_doc().splitChannels();var a=[];for(var i=0;i<docs.length;i++)a.push(docs[i].name);return a;");
570
+ jsxTool("set_active_channels", "Activates channels by name (e.g. ['Red','Green']).", { names: S.array(S.string()) },
571
571
  "var d=_doc();var arr=[];for(var i=0;i<A.names.length;i++)arr.push(d.channels.getByName(A.names[i]));d.activeChannels=arr;return 'ok';");
572
- jsxTool("channel_to_selection", "Carga un canal como selección (alias de load_channel_as_selection).", { name: S.string() }, "_doc().selection.load(_doc().channels.getByName(A.name));return 'ok';");
572
+ jsxTool("channel_to_selection", "Loads a channel as a selection (alias of load_channel_as_selection).", { name: S.string() }, "_doc().selection.load(_doc().channels.getByName(A.name));return 'ok';");
573
573
 
574
574
  /* ====================== RUTAS (PathItems) ====================== */
575
- jsxTool("list_paths", "Lista las rutas del documento.", {}, "var a=[];var p=_doc().pathItems;for(var i=0;i<p.length;i++)a.push({name:p[i].name,kind:String(p[i].kind).replace('PathKind.','')});return a;");
576
- jsxTool("delete_path", "Elimina una ruta por nombre.", { name: S.string() }, "_doc().pathItems.getByName(A.name).remove();return 'ok';");
577
- jsxTool("duplicate_path", "Duplica una ruta.", { name: S.string(), newName: S.string().optional() }, "var p=_doc().pathItems.getByName(A.name).duplicate(A.newName||undefined);return 'ok: '+p.name;");
578
- jsxTool("path_to_selection", "Convierte una ruta en selección.", { name: S.string(), feather: S.number().optional() }, "_doc().pathItems.getByName(A.name).makeSelection(A.feather||0,true,SelectionType.REPLACE);return 'ok';");
579
- jsxTool("fill_path", "Rellena una ruta con un color.", { name: S.string(), color: S.string() }, "_doc().pathItems.getByName(A.name).fillPath(_hex(A.color));return 'ok';");
580
- jsxTool("stroke_path", "Traza una ruta con la herramienta (PENCIL|BRUSH).", { name: S.string(), tool: S.string().optional() }, "_doc().pathItems.getByName(A.name).strokePath(ToolType[A.tool||'PENCIL']);return 'ok';");
581
- jsxTool("create_path_from_points", "Crea una ruta desde puntos [[x,y],...] (closed opcional).", { name: S.string(), points: S.array(S.array(S.number())), closed: S.boolean().optional() },
582
- "var pts=A.points||[];if(pts.length<2)throw 'mínimo 2 puntos';var pp=[];for(var i=0;i<pts.length;i++){var info=new PathPointInfo();info.kind=PointKind.CORNERPOINT;info.anchor=[pts[i][0],pts[i][1]];info.leftDirection=info.anchor;info.rightDirection=info.anchor;pp.push(info);}var sp=new SubPathInfo();sp.closed=!!A.closed;sp.operation=ShapeOperation.SHAPEADD;sp.entireSubPath=pp;var p=_doc().pathItems.add(A.name||'Path',[sp]);return 'ruta: '+p.name;");
575
+ jsxTool("list_paths", "Lists the document's paths.", {}, "var a=[];var p=_doc().pathItems;for(var i=0;i<p.length;i++)a.push({name:p[i].name,kind:String(p[i].kind).replace('PathKind.','')});return a;");
576
+ jsxTool("delete_path", "Deletes a path by name.", { name: S.string() }, "_doc().pathItems.getByName(A.name).remove();return 'ok';");
577
+ jsxTool("duplicate_path", "Duplicates a path.", { name: S.string(), newName: S.string().optional() }, "var p=_doc().pathItems.getByName(A.name).duplicate(A.newName||undefined);return 'ok: '+p.name;");
578
+ jsxTool("path_to_selection", "Converts a path into a selection.", { name: S.string(), feather: S.number().optional() }, "_doc().pathItems.getByName(A.name).makeSelection(A.feather||0,true,SelectionType.REPLACE);return 'ok';");
579
+ jsxTool("fill_path", "Fills a path with a color.", { name: S.string(), color: S.string() }, "_doc().pathItems.getByName(A.name).fillPath(_hex(A.color));return 'ok';");
580
+ jsxTool("stroke_path", "Strokes a path with the tool (PENCIL|BRUSH).", { name: S.string(), tool: S.string().optional() }, "_doc().pathItems.getByName(A.name).strokePath(ToolType[A.tool||'PENCIL']);return 'ok';");
581
+ jsxTool("create_path_from_points", "Creates a path from points [[x,y],...] (closed optional).", { name: S.string(), points: S.array(S.array(S.number())), closed: S.boolean().optional() },
582
+ "var pts=A.points||[];if(pts.length<2)throw 'minimum 2 points';var pp=[];for(var i=0;i<pts.length;i++){var info=new PathPointInfo();info.kind=PointKind.CORNERPOINT;info.anchor=[pts[i][0],pts[i][1]];info.leftDirection=info.anchor;info.rightDirection=info.anchor;pp.push(info);}var sp=new SubPathInfo();sp.closed=!!A.closed;sp.operation=ShapeOperation.SHAPEADD;sp.entireSubPath=pp;var p=_doc().pathItems.add(A.name||'Path',[sp]);return 'path: '+p.name;");
583
583
 
584
584
  /* ====================== FORMAS VECTORIALES (shape layers) ====================== */
585
- jsxTool("add_shape_rectangle", "Crea una capa de forma rectángulo (px) con color de relleno.", { x: S.number(), y: S.number(), width: S.number(), height: S.number(), color: S.string().optional() },
586
- "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);var shp=new ActionDescriptor();shp.putUnitDouble(charIDToTypeID('Top '),charIDToTypeID('#Pxl'),A.y);shp.putUnitDouble(charIDToTypeID('Left'),charIDToTypeID('#Pxl'),A.x);shp.putUnitDouble(charIDToTypeID('Btom'),charIDToTypeID('#Pxl'),A.y+A.height);shp.putUnitDouble(charIDToTypeID('Rght'),charIDToTypeID('#Pxl'),A.x+A.width);ct.putObject(charIDToTypeID('Shp '),charIDToTypeID('Rctn'),shp);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'rectángulo';");
587
- jsxTool("add_shape_rounded_rectangle", "Crea una capa de forma rectángulo redondeado (radius px).", { x: S.number(), y: S.number(), width: S.number(), height: S.number(), radius: S.number().optional(), color: S.string().optional() },
588
- "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);var shp=new ActionDescriptor();var rad=A.radius||10;shp.putUnitDouble(charIDToTypeID('Top '),charIDToTypeID('#Pxl'),A.y);shp.putUnitDouble(charIDToTypeID('Left'),charIDToTypeID('#Pxl'),A.x);shp.putUnitDouble(charIDToTypeID('Btom'),charIDToTypeID('#Pxl'),A.y+A.height);shp.putUnitDouble(charIDToTypeID('Rght'),charIDToTypeID('#Pxl'),A.x+A.width);shp.putUnitDouble(stringIDToTypeID('topLeft'),charIDToTypeID('#Pxl'),rad);shp.putUnitDouble(stringIDToTypeID('topRight'),charIDToTypeID('#Pxl'),rad);shp.putUnitDouble(stringIDToTypeID('bottomLeft'),charIDToTypeID('#Pxl'),rad);shp.putUnitDouble(stringIDToTypeID('bottomRight'),charIDToTypeID('#Pxl'),rad);ct.putObject(charIDToTypeID('Shp '),stringIDToTypeID('rectangle'),shp);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'rect redondeado';");
589
- jsxTool("add_shape_ellipse", "Crea una capa de forma elipse (bounding box px) con color de relleno.", { x: S.number(), y: S.number(), width: S.number(), height: S.number(), color: S.string().optional() },
590
- "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);var shp=new ActionDescriptor();shp.putUnitDouble(charIDToTypeID('Top '),charIDToTypeID('#Pxl'),A.y);shp.putUnitDouble(charIDToTypeID('Left'),charIDToTypeID('#Pxl'),A.x);shp.putUnitDouble(charIDToTypeID('Btom'),charIDToTypeID('#Pxl'),A.y+A.height);shp.putUnitDouble(charIDToTypeID('Rght'),charIDToTypeID('#Pxl'),A.x+A.width);ct.putObject(charIDToTypeID('Shp '),charIDToTypeID('Elps'),shp);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'elipse';");
585
+ jsxTool("add_shape_rectangle", "Creates a rectangle shape layer (px) with fill color.", { x: S.number(), y: S.number(), width: S.number(), height: S.number(), color: S.string().optional() },
586
+ "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);var shp=new ActionDescriptor();shp.putUnitDouble(charIDToTypeID('Top '),charIDToTypeID('#Pxl'),A.y);shp.putUnitDouble(charIDToTypeID('Left'),charIDToTypeID('#Pxl'),A.x);shp.putUnitDouble(charIDToTypeID('Btom'),charIDToTypeID('#Pxl'),A.y+A.height);shp.putUnitDouble(charIDToTypeID('Rght'),charIDToTypeID('#Pxl'),A.x+A.width);ct.putObject(charIDToTypeID('Shp '),charIDToTypeID('Rctn'),shp);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'rectangle';");
587
+ jsxTool("add_shape_rounded_rectangle", "Creates a rounded-rectangle shape layer (radius px).", { x: S.number(), y: S.number(), width: S.number(), height: S.number(), radius: S.number().optional(), color: S.string().optional() },
588
+ "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);var shp=new ActionDescriptor();var rad=A.radius||10;shp.putUnitDouble(charIDToTypeID('Top '),charIDToTypeID('#Pxl'),A.y);shp.putUnitDouble(charIDToTypeID('Left'),charIDToTypeID('#Pxl'),A.x);shp.putUnitDouble(charIDToTypeID('Btom'),charIDToTypeID('#Pxl'),A.y+A.height);shp.putUnitDouble(charIDToTypeID('Rght'),charIDToTypeID('#Pxl'),A.x+A.width);shp.putUnitDouble(stringIDToTypeID('topLeft'),charIDToTypeID('#Pxl'),rad);shp.putUnitDouble(stringIDToTypeID('topRight'),charIDToTypeID('#Pxl'),rad);shp.putUnitDouble(stringIDToTypeID('bottomLeft'),charIDToTypeID('#Pxl'),rad);shp.putUnitDouble(stringIDToTypeID('bottomRight'),charIDToTypeID('#Pxl'),rad);ct.putObject(charIDToTypeID('Shp '),stringIDToTypeID('rectangle'),shp);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'rounded rect';");
589
+ jsxTool("add_shape_ellipse", "Creates an ellipse shape layer (bounding box px) with fill color.", { x: S.number(), y: S.number(), width: S.number(), height: S.number(), color: S.string().optional() },
590
+ "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);var shp=new ActionDescriptor();shp.putUnitDouble(charIDToTypeID('Top '),charIDToTypeID('#Pxl'),A.y);shp.putUnitDouble(charIDToTypeID('Left'),charIDToTypeID('#Pxl'),A.x);shp.putUnitDouble(charIDToTypeID('Btom'),charIDToTypeID('#Pxl'),A.y+A.height);shp.putUnitDouble(charIDToTypeID('Rght'),charIDToTypeID('#Pxl'),A.x+A.width);ct.putObject(charIDToTypeID('Shp '),charIDToTypeID('Elps'),shp);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'ellipse';");
591
591
 
592
592
  /* ====================== TRANSFORM (más) ====================== */
593
- jsxTool("align_layer_to_canvas", "Alinea una capa al lienzo (edge: center|left|right|top|bottom|topleft…).", { name: S.string(), edge: S.string() },
593
+ jsxTool("align_layer_to_canvas", "Aligns a layer to the canvas (edge: center|left|right|top|bottom|topleft…).", { name: S.string(), edge: S.string() },
594
594
  "var d=_doc();var L=_need(A.name);var b=L.bounds;var x1=b[0].as('px'),y1=b[1].as('px'),x2=b[2].as('px'),y2=b[3].as('px');var w=x2-x1,h=y2-y1;var dw=d.width.as?d.width.as('px'):Math.round(d.width),dh=d.height.as?d.height.as('px'):Math.round(d.height);var tx=x1,ty=y1;var e=String(A.edge).toLowerCase();if(e.indexOf('left')>=0)tx=0;else if(e.indexOf('right')>=0)tx=dw-w;else tx=(dw-w)/2;if(e.indexOf('top')>=0)ty=0;else if(e.indexOf('bottom')>=0)ty=dh-h;else ty=(dh-h)/2;L.translate(tx-x1,ty-y1);return 'ok';");
595
- jsxTool("free_transform_layer", "Transforma una capa: escala % + rotación + desplazamiento en un paso.", { name: S.string(), scaleW: S.number().optional(), scaleH: S.number().optional(), angle: S.number().optional(), dx: S.number().optional(), dy: S.number().optional() },
595
+ jsxTool("free_transform_layer", "Transforms a layer: scale % + rotation + offset in one step.", { name: S.string(), scaleW: S.number().optional(), scaleH: S.number().optional(), angle: S.number().optional(), dx: S.number().optional(), dy: S.number().optional() },
596
596
  "var L=_need(A.name);if(A.scaleW||A.scaleH)L.resize(A.scaleW||100,A.scaleH||100,AnchorPosition.MIDDLECENTER);if(A.angle)L.rotate(A.angle,AnchorPosition.MIDDLECENTER);if(A.dx||A.dy)L.translate(A.dx||0,A.dy||0);return 'ok';");
597
- jsxTool("flip_canvas", "Voltea el lienzo entero (axis: horizontal|vertical).", { axis: S.string().optional() },
597
+ jsxTool("flip_canvas", "Flips the entire canvas (axis: horizontal|vertical).", { axis: S.string().optional() },
598
598
  "var d=new ActionDescriptor();d.putEnumerated(stringIDToTypeID('axis'),stringIDToTypeID('orientation'),A.axis==='vertical'?stringIDToTypeID('vertical'):stringIDToTypeID('horizontal'));executeAction(stringIDToTypeID('flipCanvas'),d,DialogModes.NO);return 'ok';");
599
599
 
600
600
  /* ====================== ESTILOS DE CAPA (completos, mergean con el estilo existente) ====================== */
601
- jsxTool("add_inner_shadow", "Sombra interior.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), angle: S.number().optional(), distance: S.number().optional(), size: S.number().optional() },
602
- "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Mltp'));var c=new ActionDescriptor();c.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#000000'));e.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#000000'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||75);e.putUnitDouble(charIDToTypeID('lagl'),charIDToTypeID('#Ang'),A.angle||120);e.putUnitDouble(charIDToTypeID('Dstn'),charIDToTypeID('#Pxl'),A.distance||5);e.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||5);_mergeFx(charIDToTypeID('IrSh'),e);return 'sombra interior';");
603
- jsxTool("add_outer_glow", "Resplandor exterior.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), size: S.number().optional() },
604
- "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Scrn'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||75);e.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#FFFFBE'));e.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||10);_mergeFx(charIDToTypeID('OrGl'),e);return 'resplandor exterior';");
605
- jsxTool("add_inner_glow", "Resplandor interior.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), size: S.number().optional() },
606
- "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Scrn'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||75);e.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#FFFFBE'));e.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||10);_mergeFx(charIDToTypeID('IrGl'),e);return 'resplandor interior';");
607
- jsxTool("add_satin", "Satén (Satin).", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), angle: S.number().optional(), distance: S.number().optional(), size: S.number().optional() },
608
- "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Mltp'));e.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#000000'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||50);e.putUnitDouble(charIDToTypeID('lagl'),charIDToTypeID('#Ang'),A.angle||19);e.putUnitDouble(charIDToTypeID('Dstn'),charIDToTypeID('#Pxl'),A.distance||11);e.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||14);e.putBoolean(charIDToTypeID('AntA'),true);_mergeFx(charIDToTypeID('ChFX'),e);return 'satén';");
609
- jsxTool("add_bevel_emboss", "Bisel y relieve.", { name: S.string().optional(), size: S.number().optional(), depth: S.number().optional(), angle: S.number().optional(), altitude: S.number().optional() },
610
- "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('bvlS'),charIDToTypeID('BESl'),charIDToTypeID('InrB'));e.putEnumerated(charIDToTypeID('bvlT'),charIDToTypeID('bvlT'),charIDToTypeID('SfBL'));e.putUnitDouble(charIDToTypeID('Sz '),charIDToTypeID('#Pxl'),A.size||5);e.putUnitDouble(charIDToTypeID('srgR'),charIDToTypeID('#Prc'),A.depth||100);e.putUnitDouble(charIDToTypeID('lagl'),charIDToTypeID('#Ang'),A.angle||120);e.putUnitDouble(charIDToTypeID('Lald'),charIDToTypeID('#Ang'),A.altitude||30);e.putEnumerated(charIDToTypeID('hglM'),charIDToTypeID('BlnM'),charIDToTypeID('Scrn'));e.putObject(charIDToTypeID('hglC'),charIDToTypeID('RGBC'),_cD('#FFFFFF'));e.putUnitDouble(charIDToTypeID('hglO'),charIDToTypeID('#Prc'),75);e.putEnumerated(charIDToTypeID('sdwM'),charIDToTypeID('BlnM'),charIDToTypeID('Mltp'));e.putObject(charIDToTypeID('sdwC'),charIDToTypeID('RGBC'),_cD('#000000'));e.putUnitDouble(charIDToTypeID('sdwO'),charIDToTypeID('#Prc'),75);_mergeFx(charIDToTypeID('ebbl'),e);return 'bisel';");
611
- jsxTool("add_gradient_overlay", "Superposición de degradado (from->to hex).", { name: S.string().optional(), from: S.string().optional(), to: S.string().optional(), opacity: S.number().optional(), angle: S.number().optional() },
612
- "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Nrml'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||100);e.putObject(charIDToTypeID('Grad'),charIDToTypeID('Grdn'),_grad(A.from||'#000000',A.to||'#FFFFFF'));e.putEnumerated(charIDToTypeID('Type'),charIDToTypeID('GrdT'),charIDToTypeID('Lnr '));e.putUnitDouble(charIDToTypeID('Angl'),charIDToTypeID('#Ang'),A.angle||90);_mergeFx(charIDToTypeID('GrFl'),e);return 'degradado';");
613
- jsxTool("get_layer_effects", "Lee los estilos de capa presentes y si están activos (incluye el ojo maestro fxVisible).", { name: S.string().optional() },
601
+ jsxTool("add_inner_shadow", "Inner shadow.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), angle: S.number().optional(), distance: S.number().optional(), size: S.number().optional() },
602
+ "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Mltp'));var c=new ActionDescriptor();c.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#000000'));e.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#000000'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||75);e.putUnitDouble(charIDToTypeID('lagl'),charIDToTypeID('#Ang'),A.angle||120);e.putUnitDouble(charIDToTypeID('Dstn'),charIDToTypeID('#Pxl'),A.distance||5);e.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||5);_mergeFx(charIDToTypeID('IrSh'),e);return 'inner shadow';");
603
+ jsxTool("add_outer_glow", "Outer glow.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), size: S.number().optional() },
604
+ "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Scrn'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||75);e.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#FFFFBE'));e.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||10);_mergeFx(charIDToTypeID('OrGl'),e);return 'outer glow';");
605
+ jsxTool("add_inner_glow", "Inner glow.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), size: S.number().optional() },
606
+ "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Scrn'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||75);e.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#FFFFBE'));e.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||10);_mergeFx(charIDToTypeID('IrGl'),e);return 'inner glow';");
607
+ jsxTool("add_satin", "Satin.", { name: S.string().optional(), color: S.string().optional(), opacity: S.number().optional(), angle: S.number().optional(), distance: S.number().optional(), size: S.number().optional() },
608
+ "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Mltp'));e.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#000000'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||50);e.putUnitDouble(charIDToTypeID('lagl'),charIDToTypeID('#Ang'),A.angle||19);e.putUnitDouble(charIDToTypeID('Dstn'),charIDToTypeID('#Pxl'),A.distance||11);e.putUnitDouble(charIDToTypeID('blur'),charIDToTypeID('#Pxl'),A.size||14);e.putBoolean(charIDToTypeID('AntA'),true);_mergeFx(charIDToTypeID('ChFX'),e);return 'satin';");
609
+ jsxTool("add_bevel_emboss", "Bevel & emboss.", { name: S.string().optional(), size: S.number().optional(), depth: S.number().optional(), angle: S.number().optional(), altitude: S.number().optional() },
610
+ "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('bvlS'),charIDToTypeID('BESl'),charIDToTypeID('InrB'));e.putEnumerated(charIDToTypeID('bvlT'),charIDToTypeID('bvlT'),charIDToTypeID('SfBL'));e.putUnitDouble(charIDToTypeID('Sz '),charIDToTypeID('#Pxl'),A.size||5);e.putUnitDouble(charIDToTypeID('srgR'),charIDToTypeID('#Prc'),A.depth||100);e.putUnitDouble(charIDToTypeID('lagl'),charIDToTypeID('#Ang'),A.angle||120);e.putUnitDouble(charIDToTypeID('Lald'),charIDToTypeID('#Ang'),A.altitude||30);e.putEnumerated(charIDToTypeID('hglM'),charIDToTypeID('BlnM'),charIDToTypeID('Scrn'));e.putObject(charIDToTypeID('hglC'),charIDToTypeID('RGBC'),_cD('#FFFFFF'));e.putUnitDouble(charIDToTypeID('hglO'),charIDToTypeID('#Prc'),75);e.putEnumerated(charIDToTypeID('sdwM'),charIDToTypeID('BlnM'),charIDToTypeID('Mltp'));e.putObject(charIDToTypeID('sdwC'),charIDToTypeID('RGBC'),_cD('#000000'));e.putUnitDouble(charIDToTypeID('sdwO'),charIDToTypeID('#Prc'),75);_mergeFx(charIDToTypeID('ebbl'),e);return 'bevel';");
611
+ jsxTool("add_gradient_overlay", "Gradient overlay (from->to hex).", { name: S.string().optional(), from: S.string().optional(), to: S.string().optional(), opacity: S.number().optional(), angle: S.number().optional() },
612
+ "if(A.name)_sel(_need(A.name));var e=new ActionDescriptor();e.putBoolean(charIDToTypeID('enab'),true);e.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Nrml'));e.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),A.opacity||100);e.putObject(charIDToTypeID('Grad'),charIDToTypeID('Grdn'),_grad(A.from||'#000000',A.to||'#FFFFFF'));e.putEnumerated(charIDToTypeID('Type'),charIDToTypeID('GrdT'),charIDToTypeID('Lnr '));e.putUnitDouble(charIDToTypeID('Angl'),charIDToTypeID('#Ang'),A.angle||90);_mergeFx(charIDToTypeID('GrFl'),e);return 'gradient';");
613
+ jsxTool("get_layer_effects", "Reads the layer styles present and whether they're active (includes the master fxVisible eye).", { name: S.string().optional() },
614
614
  "if(A.name)_sel(_need(A.name));var d=executeActionGet(_layerRef());var out={fxVisible:(d.hasKey(stringIDToTypeID('layerFXVisible'))?d.getBoolean(stringIDToTypeID('layerFXVisible')):true),effects:[]};if(!d.hasKey(stringIDToTypeID('layerEffects')))return out;var fx=d.getObjectValue(stringIDToTypeID('layerEffects'));var map={DrSh:'dropShadow',IrSh:'innerShadow',OrGl:'outerGlow',IrGl:'innerGlow',ebbl:'bevelEmboss',ChFX:'satin',SoFi:'colorOverlay',GrFl:'gradientOverlay',patternFill:'patternOverlay',FrFX:'stroke'};for(var k in map){var id;try{id=charIDToTypeID(k);}catch(e){id=stringIDToTypeID(k);}if(fx.hasKey(id)){var o=fx.getObjectValue(id);if(o.typename==='ActionList')o=o.getObjectValue(0);var en=o.hasKey(charIDToTypeID('enab'))?o.getBoolean(charIDToTypeID('enab')):true;out.effects.push({effect:map[k],enabled:en});}}return out;");
615
- jsxTool("apply_style_by_name", "Aplica un estilo de capa guardado por nombre (panel Estilos).", { name: S.string().optional(), style: S.string() }, "_act(A.name).applyStyle(A.style);return 'ok';");
616
- jsxTool("scale_layer_effects", "Escala los estilos de la capa (porcentaje).", { name: S.string().optional(), scale: S.number() },
615
+ jsxTool("apply_style_by_name", "Applies a saved layer style by name (Styles panel).", { name: S.string().optional(), style: S.string() }, "_act(A.name).applyStyle(A.style);return 'ok';");
616
+ jsxTool("scale_layer_effects", "Scales the layer's styles (percentage).", { name: S.string().optional(), scale: S.number() },
617
617
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);d.putUnitDouble(charIDToTypeID('Scl '),charIDToTypeID('#Prc'),A.scale||100);executeAction(stringIDToTypeID('scaleEffectsEvent'),d,DialogModes.NO);return 'ok';");
618
618
 
619
619
  /* ====================== AJUSTES CON PARÁMETROS (destructivos) ====================== */
620
- jsxTool("adjust_curves", "Curvas sobre la capa: puntos [[in,out],...] en el canal compuesto.", { points: S.array(S.array(S.number())), name: S.string().optional() },
620
+ jsxTool("adjust_curves", "Curves on the layer: points [[in,out],...] on the composite channel.", { points: S.array(S.array(S.number())), name: S.string().optional() },
621
621
  "_act(A.name).adjustCurves(A.points);return 'ok';");
622
- jsxTool("adjust_hue_saturation", "Tono/Saturación/Luminosidad (destructivo).", { hue: S.number().optional(), saturation: S.number().optional(), lightness: S.number().optional(), colorize: S.boolean().optional(), name: S.string().optional() },
622
+ jsxTool("adjust_hue_saturation", "Hue/Saturation/Lightness (destructive).", { hue: S.number().optional(), saturation: S.number().optional(), lightness: S.number().optional(), colorize: S.boolean().optional(), name: S.string().optional() },
623
623
  "if(A.name)_sel(_need(A.name));var a=new ActionDescriptor();a.putEnumerated(stringIDToTypeID('presetKind'),stringIDToTypeID('presetKindType'),stringIDToTypeID('presetKindCustom'));a.putBoolean(charIDToTypeID('Clrz'),!!A.colorize);var list=new ActionList();var ch=new ActionDescriptor();ch.putInteger(charIDToTypeID('H '),A.hue||0);ch.putInteger(charIDToTypeID('Strt'),A.saturation||0);ch.putInteger(charIDToTypeID('Lght'),A.lightness||0);list.putObject(charIDToTypeID('Hst2'),ch);a.putList(charIDToTypeID('Adjs'),list);executeAction(charIDToTypeID('HStr'),a,DialogModes.NO);return 'ok';");
624
- jsxTool("adjust_vibrance", "Vibrancia/Saturación (destructivo).", { vibrance: S.number().optional(), saturation: S.number().optional(), name: S.string().optional() },
624
+ jsxTool("adjust_vibrance", "Vibrance/Saturation (destructive).", { vibrance: S.number().optional(), saturation: S.number().optional(), name: S.string().optional() },
625
625
  "if(A.name)_sel(_need(A.name));var a=new ActionDescriptor();if(A.vibrance!=null)a.putInteger(stringIDToTypeID('vibrance'),A.vibrance);if(A.saturation!=null)a.putInteger(charIDToTypeID('Strt'),A.saturation);executeAction(stringIDToTypeID('vibrance'),a,DialogModes.NO);return 'ok';");
626
- jsxTool("adjust_exposure", "Exposición/Desplazamiento/Gamma (destructivo).", { exposure: S.number().optional(), offset: S.number().optional(), gamma: S.number().optional(), name: S.string().optional() },
626
+ jsxTool("adjust_exposure", "Exposure/Offset/Gamma (destructive).", { exposure: S.number().optional(), offset: S.number().optional(), gamma: S.number().optional(), name: S.string().optional() },
627
627
  "if(A.name)_sel(_need(A.name));var a=new ActionDescriptor();a.putEnumerated(stringIDToTypeID('presetKind'),stringIDToTypeID('presetKindType'),stringIDToTypeID('presetKindCustom'));a.putUnitDouble(stringIDToTypeID('exposure'),charIDToTypeID('#Pxl'),A.exposure||0);a.putDouble(charIDToTypeID('Ofst'),A.offset||0);a.putDouble(stringIDToTypeID('gammaCorrection'),A.gamma||1);executeAction(stringIDToTypeID('exposure'),a,DialogModes.NO);return 'ok';");
628
- jsxTool("adjust_photo_filter", "Filtro de foto (color + densidad %).", { color: S.string().optional(), density: S.number().optional(), preserveLuminosity: S.boolean().optional(), name: S.string().optional() },
628
+ jsxTool("adjust_photo_filter", "Photo filter (color + density %).", { color: S.string().optional(), density: S.number().optional(), preserveLuminosity: S.boolean().optional(), name: S.string().optional() },
629
629
  "if(A.name)_sel(_need(A.name));var a=new ActionDescriptor();a.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#EC8A00'));a.putInteger(charIDToTypeID('Dnst'),A.density!=null?A.density:25);a.putBoolean(charIDToTypeID('PrsL'),A.preserveLuminosity!==false);executeAction(stringIDToTypeID('photoFilter'),a,DialogModes.NO);return 'ok';");
630
- jsxTool("adjust_shadow_highlight", "Sombras/Iluminaciones automático (destructivo, valores por defecto o custom).", { shadows: S.number().optional(), highlights: S.number().optional(), name: S.string().optional() },
630
+ jsxTool("adjust_shadow_highlight", "Shadows/Highlights auto (destructive, default or custom values).", { shadows: S.number().optional(), highlights: S.number().optional(), name: S.string().optional() },
631
631
  "if(A.name)_sel(_need(A.name));var a=new ActionDescriptor();a.putUnitDouble(stringIDToTypeID('shadowAmount'),charIDToTypeID('#Prc'),A.shadows!=null?A.shadows:35);a.putUnitDouble(stringIDToTypeID('highlightAmount'),charIDToTypeID('#Prc'),A.highlights!=null?A.highlights:0);executeAction(stringIDToTypeID('shadowHighlight'),a,DialogModes.NO);return 'ok';");
632
- jsxTool("black_and_white", "Blanco y negro (destructivo, mezcla por defecto).", { name: S.string().optional() },
632
+ jsxTool("black_and_white", "Black & white (destructive, default mix).", { name: S.string().optional() },
633
633
  "if(A.name)_sel(_need(A.name));var a=new ActionDescriptor();a.putEnumerated(stringIDToTypeID('presetKind'),stringIDToTypeID('presetKindType'),stringIDToTypeID('presetKindDefault'));executeAction(stringIDToTypeID('blackAndWhite'),a,DialogModes.NO);return 'ok';");
634
634
 
635
635
  /* ====================== CAPAS DE AJUSTE (parametrizadas, no destructivas) ====================== */
636
- jsxTool("add_adjustment_brightness_contrast", "Capa de ajuste Brillo/Contraste.", { brightness: S.number().optional(), contrast: S.number().optional() },
636
+ jsxTool("add_adjustment_brightness_contrast", "Brightness/Contrast adjustment layer.", { brightness: S.number().optional(), contrast: S.number().optional() },
637
637
  "var a=new ActionDescriptor();a.putInteger(charIDToTypeID('Brgh'),A.brightness||0);a.putInteger(charIDToTypeID('Cntr'),A.contrast||0);a.putBoolean(stringIDToTypeID('useLegacy'),false);_mkAdj(stringIDToTypeID('brightnessEvent'),a);return 'ok';");
638
- jsxTool("add_adjustment_hue_saturation", "Capa de ajuste Tono/Saturación.", { hue: S.number().optional(), saturation: S.number().optional(), lightness: S.number().optional(), colorize: S.boolean().optional() },
638
+ jsxTool("add_adjustment_hue_saturation", "Hue/Saturation adjustment layer.", { hue: S.number().optional(), saturation: S.number().optional(), lightness: S.number().optional(), colorize: S.boolean().optional() },
639
639
  "var a=new ActionDescriptor();a.putEnumerated(stringIDToTypeID('presetKind'),stringIDToTypeID('presetKindType'),stringIDToTypeID('presetKindCustom'));a.putBoolean(charIDToTypeID('Clrz'),!!A.colorize);var list=new ActionList();var ch=new ActionDescriptor();ch.putInteger(charIDToTypeID('H '),A.hue||0);ch.putInteger(charIDToTypeID('Strt'),A.saturation||0);ch.putInteger(charIDToTypeID('Lght'),A.lightness||0);list.putObject(charIDToTypeID('Hst2'),ch);a.putList(charIDToTypeID('Adjs'),list);_mkAdj(stringIDToTypeID('hueSaturation'),a);return 'ok';");
640
- jsxTool("add_adjustment_vibrance", "Capa de ajuste Vibrancia.", { vibrance: S.number().optional(), saturation: S.number().optional() },
640
+ jsxTool("add_adjustment_vibrance", "Vibrance adjustment layer.", { vibrance: S.number().optional(), saturation: S.number().optional() },
641
641
  "var a=new ActionDescriptor();if(A.vibrance!=null)a.putInteger(stringIDToTypeID('vibrance'),A.vibrance);if(A.saturation!=null)a.putInteger(charIDToTypeID('Strt'),A.saturation);_mkAdj(stringIDToTypeID('vibrance'),a);return 'ok';");
642
- jsxTool("add_adjustment_exposure", "Capa de ajuste Exposición.", { exposure: S.number().optional(), offset: S.number().optional(), gamma: S.number().optional() },
642
+ jsxTool("add_adjustment_exposure", "Exposure adjustment layer.", { exposure: S.number().optional(), offset: S.number().optional(), gamma: S.number().optional() },
643
643
  "var a=new ActionDescriptor();a.putEnumerated(stringIDToTypeID('presetKind'),stringIDToTypeID('presetKindType'),stringIDToTypeID('presetKindCustom'));a.putUnitDouble(stringIDToTypeID('exposure'),charIDToTypeID('#Pxl'),A.exposure||0);a.putDouble(charIDToTypeID('Ofst'),A.offset||0);a.putDouble(stringIDToTypeID('gammaCorrection'),A.gamma||1);_mkAdj(stringIDToTypeID('exposure'),a);return 'ok';");
644
- jsxTool("add_adjustment_photo_filter", "Capa de ajuste Filtro de foto.", { color: S.string().optional(), density: S.number().optional(), preserveLuminosity: S.boolean().optional() },
644
+ jsxTool("add_adjustment_photo_filter", "Photo Filter adjustment layer.", { color: S.string().optional(), density: S.number().optional(), preserveLuminosity: S.boolean().optional() },
645
645
  "var a=new ActionDescriptor();a.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#EC8A00'));a.putInteger(charIDToTypeID('Dnst'),A.density!=null?A.density:25);a.putBoolean(charIDToTypeID('PrsL'),A.preserveLuminosity!==false);_mkAdj(stringIDToTypeID('photoFilter'),a);return 'ok';");
646
- jsxTool("add_adjustment_black_white", "Capa de ajuste Blanco y negro.", {}, "var a=new ActionDescriptor();a.putEnumerated(stringIDToTypeID('presetKind'),stringIDToTypeID('presetKindType'),stringIDToTypeID('presetKindDefault'));_mkAdj(stringIDToTypeID('blackAndWhite'),a);return 'ok';");
647
- jsxTool("add_adjustment_gradient_map", "Capa de ajuste Mapa de degradado (from->to hex).", { from: S.string().optional(), to: S.string().optional() },
646
+ jsxTool("add_adjustment_black_white", "Black & White adjustment layer.", {}, "var a=new ActionDescriptor();a.putEnumerated(stringIDToTypeID('presetKind'),stringIDToTypeID('presetKindType'),stringIDToTypeID('presetKindDefault'));_mkAdj(stringIDToTypeID('blackAndWhite'),a);return 'ok';");
647
+ jsxTool("add_adjustment_gradient_map", "Gradient Map adjustment layer (from->to hex).", { from: S.string().optional(), to: S.string().optional() },
648
648
  "var a=new ActionDescriptor();a.putObject(charIDToTypeID('Grad'),charIDToTypeID('Grdn'),_grad(A.from||'#000000',A.to||'#FFFFFF'));_mkAdj(stringIDToTypeID('gradientMapClass'),a);return 'ok';");
649
- jsxTool("add_solid_color_fill", "Capa de relleno de color sólido (todo el lienzo).", { color: S.string().optional() },
649
+ jsxTool("add_solid_color_fill", "Solid Color fill layer (entire canvas).", { color: S.string().optional() },
650
650
  "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'ok';");
651
- jsxTool("add_gradient_fill", "Capa de relleno de degradado (from->to hex, angle).", { from: S.string().optional(), to: S.string().optional(), angle: S.number().optional() },
651
+ jsxTool("add_gradient_fill", "Gradient fill layer (from->to hex, angle).", { from: S.string().optional(), to: S.string().optional(), angle: S.number().optional() },
652
652
  "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var g=new ActionDescriptor();g.putUnitDouble(charIDToTypeID('Angl'),charIDToTypeID('#Ang'),A.angle||90);g.putEnumerated(charIDToTypeID('Type'),charIDToTypeID('GrdT'),charIDToTypeID('Lnr '));g.putObject(charIDToTypeID('Grad'),charIDToTypeID('Grdn'),_grad(A.from||'#000000',A.to||'#FFFFFF'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('gradientLayer'),g);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'ok';");
653
653
 
654
654
  /* ====================== FILTROS (DOM extra) ====================== */
655
- jsxTool("apply_average", "Promedio (rellena con el color promedio).", { name: S.string().optional() }, "_act(A.name).applyAverage();return 'ok';");
656
- jsxTool("apply_grain", "Grano (intensidad, contraste, tipo).", { intensity: S.number().optional(), contrast: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyGrain(A.intensity||40,A.contrast||50,GrainType.REGULAR);return 'ok';");
657
- jsxTool("apply_ntsc", "Colores NTSC.", { name: S.string().optional() }, "_act(A.name).applyNTSC();return 'ok';");
658
- jsxTool("apply_deinterlace", "Desentrelazar.", { name: S.string().optional() }, "_act(A.name).applyDeInterlace(EliminateFields.ODDFIELDS,CreateFields.DUPLICATION);return 'ok';");
659
- jsxTool("apply_wave", "Onda (Wave).", { amplitudeMin: S.number().optional(), amplitudeMax: S.number().optional(), wavelengthMin: S.number().optional(), wavelengthMax: S.number().optional(), name: S.string().optional() },
655
+ jsxTool("apply_average", "Average (fills with the average color).", { name: S.string().optional() }, "_act(A.name).applyAverage();return 'ok';");
656
+ jsxTool("apply_grain", "Grain (intensity, contrast, type).", { intensity: S.number().optional(), contrast: S.number().optional(), name: S.string().optional() }, "_act(A.name).applyGrain(A.intensity||40,A.contrast||50,GrainType.REGULAR);return 'ok';");
657
+ jsxTool("apply_ntsc", "NTSC Colors.", { name: S.string().optional() }, "_act(A.name).applyNTSC();return 'ok';");
658
+ jsxTool("apply_deinterlace", "De-Interlace.", { name: S.string().optional() }, "_act(A.name).applyDeInterlace(EliminateFields.ODDFIELDS,CreateFields.DUPLICATION);return 'ok';");
659
+ jsxTool("apply_wave", "Wave.", { amplitudeMin: S.number().optional(), amplitudeMax: S.number().optional(), wavelengthMin: S.number().optional(), wavelengthMax: S.number().optional(), name: S.string().optional() },
660
660
  "_act(A.name).applyWave(5,A.wavelengthMin||10,A.wavelengthMax||120,A.amplitudeMin||5,A.amplitudeMax||35,100,100,WaveType.SINE,UndefinedAreas.WRAPAROUND,0);return 'ok';");
661
661
 
662
662
  /* ====================== FILTROS (Action Manager) ====================== */
663
- jsxTool("apply_mosaic", "Mosaico (tamaño de celda px).", { cellSize: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('ClSz'),charIDToTypeID('#Pxl'),A.cellSize||10);executeAction(charIDToTypeID('Msc '),d,DialogModes.NO);return 'ok';");
664
- jsxTool("apply_crystallize", "Cristalizar (tamaño de celda).", { cellSize: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putInteger(charIDToTypeID('ClSz'),A.cellSize||10);executeAction(charIDToTypeID('Crst'),d,DialogModes.NO);return 'ok';");
665
- jsxTool("apply_pointillize", "Puntillismo (tamaño de celda).", { cellSize: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putInteger(charIDToTypeID('ClSz'),A.cellSize||5);executeAction(charIDToTypeID('Pntl'),d,DialogModes.NO);return 'ok';");
666
- jsxTool("apply_color_halftone", "Semitono de color (radio máx px).", { radius: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putInteger(charIDToTypeID('Rds '),A.radius||8);d.putInteger(charIDToTypeID('Chn1'),108);d.putInteger(charIDToTypeID('Chn2'),162);d.putInteger(charIDToTypeID('Chn3'),90);d.putInteger(charIDToTypeID('Chn4'),45);executeAction(charIDToTypeID('ClrH'),d,DialogModes.NO);return 'ok';");
667
- jsxTool("apply_facet", "Facetado.", { name: S.string().optional() }, "if(A.name)_sel(_need(A.name));executeAction(charIDToTypeID('Fct '),undefined,DialogModes.NO);return 'ok';");
668
- jsxTool("apply_fragment", "Fragmentar.", { name: S.string().optional() }, "if(A.name)_sel(_need(A.name));executeAction(charIDToTypeID('Frgm'),undefined,DialogModes.NO);return 'ok';");
669
- jsxTool("apply_find_edges", "Hallar bordes.", { name: S.string().optional() }, "if(A.name)_sel(_need(A.name));executeAction(charIDToTypeID('FndE'),undefined,DialogModes.NO);return 'ok';");
670
- jsxTool("apply_emboss", "Relieve (ángulo, altura px, cantidad %).", { angle: S.number().optional(), height: S.number().optional(), amount: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putInteger(charIDToTypeID('Angl'),A.angle||135);d.putInteger(charIDToTypeID('Hght'),A.height||3);d.putInteger(charIDToTypeID('Amnt'),A.amount||100);executeAction(charIDToTypeID('Embs'),d,DialogModes.NO);return 'ok';");
671
- jsxTool("apply_smart_sharpen", "Enfoque suavizado (cantidad %, radio px, reducción de ruido %).", { amount: S.number().optional(), radius: S.number().optional(), noiseReduction: S.number().optional(), name: S.string().optional() },
663
+ jsxTool("apply_mosaic", "Mosaic (cell size px).", { cellSize: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('ClSz'),charIDToTypeID('#Pxl'),A.cellSize||10);executeAction(charIDToTypeID('Msc '),d,DialogModes.NO);return 'ok';");
664
+ jsxTool("apply_crystallize", "Crystallize (cell size).", { cellSize: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putInteger(charIDToTypeID('ClSz'),A.cellSize||10);executeAction(charIDToTypeID('Crst'),d,DialogModes.NO);return 'ok';");
665
+ jsxTool("apply_pointillize", "Pointillize (cell size).", { cellSize: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putInteger(charIDToTypeID('ClSz'),A.cellSize||5);executeAction(charIDToTypeID('Pntl'),d,DialogModes.NO);return 'ok';");
666
+ jsxTool("apply_color_halftone", "Color Halftone (max radius px).", { radius: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putInteger(charIDToTypeID('Rds '),A.radius||8);d.putInteger(charIDToTypeID('Chn1'),108);d.putInteger(charIDToTypeID('Chn2'),162);d.putInteger(charIDToTypeID('Chn3'),90);d.putInteger(charIDToTypeID('Chn4'),45);executeAction(charIDToTypeID('ClrH'),d,DialogModes.NO);return 'ok';");
667
+ jsxTool("apply_facet", "Facet.", { name: S.string().optional() }, "if(A.name)_sel(_need(A.name));executeAction(charIDToTypeID('Fct '),undefined,DialogModes.NO);return 'ok';");
668
+ jsxTool("apply_fragment", "Fragment.", { name: S.string().optional() }, "if(A.name)_sel(_need(A.name));executeAction(charIDToTypeID('Frgm'),undefined,DialogModes.NO);return 'ok';");
669
+ jsxTool("apply_find_edges", "Find Edges.", { name: S.string().optional() }, "if(A.name)_sel(_need(A.name));executeAction(charIDToTypeID('FndE'),undefined,DialogModes.NO);return 'ok';");
670
+ jsxTool("apply_emboss", "Emboss (angle, height px, amount %).", { angle: S.number().optional(), height: S.number().optional(), amount: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putInteger(charIDToTypeID('Angl'),A.angle||135);d.putInteger(charIDToTypeID('Hght'),A.height||3);d.putInteger(charIDToTypeID('Amnt'),A.amount||100);executeAction(charIDToTypeID('Embs'),d,DialogModes.NO);return 'ok';");
671
+ jsxTool("apply_smart_sharpen", "Smart Sharpen (amount %, radius px, noise reduction %).", { amount: S.number().optional(), radius: S.number().optional(), noiseReduction: S.number().optional(), name: S.string().optional() },
672
672
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putUnitDouble(stringIDToTypeID('amount'),charIDToTypeID('#Prc'),A.amount||100);d.putUnitDouble(stringIDToTypeID('radius'),charIDToTypeID('#Pxl'),A.radius||2);d.putUnitDouble(stringIDToTypeID('noiseReduction'),charIDToTypeID('#Prc'),A.noiseReduction||10);d.putEnumerated(stringIDToTypeID('blur'),stringIDToTypeID('blurType'),stringIDToTypeID('gaussianBlur'));executeAction(stringIDToTypeID('smartSharpen'),d,DialogModes.NO);return 'ok';");
673
- jsxTool("apply_surface_blur", "Desenfoque de superficie (radio px, umbral).", { radius: S.number().optional(), threshold: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('Rds '),charIDToTypeID('#Pxl'),A.radius||5);d.putInteger(charIDToTypeID('Thsh'),A.threshold||15);executeAction(stringIDToTypeID('surfaceBlur'),d,DialogModes.NO);return 'ok';");
674
- jsxTool("apply_box_blur", "Desenfoque de caja (radio px).", { radius: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('Rds '),charIDToTypeID('#Pxl'),A.radius||10);executeAction(stringIDToTypeID('boxBlur'),d,DialogModes.NO);return 'ok';");
673
+ jsxTool("apply_surface_blur", "Surface blur (radius px, threshold).", { radius: S.number().optional(), threshold: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('Rds '),charIDToTypeID('#Pxl'),A.radius||5);d.putInteger(charIDToTypeID('Thsh'),A.threshold||15);executeAction(stringIDToTypeID('surfaceBlur'),d,DialogModes.NO);return 'ok';");
674
+ jsxTool("apply_box_blur", "Box blur (radius px).", { radius: S.number().optional(), name: S.string().optional() }, "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('Rds '),charIDToTypeID('#Pxl'),A.radius||10);executeAction(stringIDToTypeID('boxBlur'),d,DialogModes.NO);return 'ok';");
675
675
 
676
676
  /* ====================== SMART OBJECTS (más) ====================== */
677
- jsxTool("new_smart_object_via_copy", "Crea un Smart Object nuevo por copia (no vinculado al original).", { name: S.string().optional() },
678
- "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('placedLayerMakeCopy'),undefined,DialogModes.NO);return 'copia SO';");
679
- jsxTool("export_smart_object_contents", "Exporta el contenido de un Smart Object a un archivo.", { name: S.string(), path: S.string() },
677
+ jsxTool("new_smart_object_via_copy", "Creates a new Smart Object by copy (not linked to the original).", { name: S.string().optional() },
678
+ "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('placedLayerMakeCopy'),undefined,DialogModes.NO);return 'SO copy';");
679
+ jsxTool("export_smart_object_contents", "Exports a Smart Object's contents to a file.", { name: S.string(), path: S.string() },
680
680
  "_sel(_need(A.name));var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));executeAction(stringIDToTypeID('placedLayerExportContents'),d,DialogModes.NO);return {path:A.path};");
681
- jsxTool("reset_smart_object_transform", "Resetea la transformación del Smart Object.", { name: S.string().optional() },
681
+ jsxTool("reset_smart_object_transform", "Resets the Smart Object's transform.", { name: S.string().optional() },
682
682
  "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('placedLayerResetTransforms'),undefined,DialogModes.NO);return 'ok';");
683
683
 
684
684
  /* ====================== COLOR / MUESTREO (más) ====================== */
685
- jsxTool("sample_region_average_color", "Color promedio de una región (x,y,w,h) → HEX. No modifica el documento.", { x: S.number(), y: S.number(), width: S.number(), height: S.number() },
685
+ jsxTool("sample_region_average_color", "Average color of a region (x,y,w,h) → HEX. Doesn't modify the document.", { x: S.number(), y: S.number(), width: S.number(), height: S.number() },
686
686
  "var d=_doc();var dup=d.duplicate();app.activeDocument=dup;dup.flatten();dup.crop([A.x,A.y,A.x+A.width,A.y+A.height]);dup.resizeImage(UnitValue(1,'px'),UnitValue(1,'px'),null,ResampleMethod.BICUBICAUTOMATIC);var s=dup.colorSamplers.add([UnitValue(0,'px'),UnitValue(0,'px')]);var hex='#'+s.color.rgb.hexValue;s.remove();dup.close(SaveOptions.DONOTSAVECHANGES);app.activeDocument=d;return {color:hex.toUpperCase()};", 180000);
687
- jsxTool("add_color_sampler", "Agrega un muestreador de color persistente en (x,y) → HEX.", { x: S.number(), y: S.number() }, "var s=_doc().colorSamplers.add([UnitValue(A.x,'px'),UnitValue(A.y,'px')]);return {color:('#'+s.color.rgb.hexValue).toUpperCase()};");
688
- jsxTool("list_color_samplers", "Lista los muestreadores de color activos.", {}, "var a=[];var cs=_doc().colorSamplers;for(var i=0;i<cs.length;i++){try{a.push({x:Math.round(cs[i].position[0].as('px')),y:Math.round(cs[i].position[1].as('px')),color:('#'+cs[i].color.rgb.hexValue).toUpperCase()});}catch(e){}}return a;");
687
+ jsxTool("add_color_sampler", "Adds a persistent color sampler at (x,y) → HEX.", { x: S.number(), y: S.number() }, "var s=_doc().colorSamplers.add([UnitValue(A.x,'px'),UnitValue(A.y,'px')]);return {color:('#'+s.color.rgb.hexValue).toUpperCase()};");
688
+ jsxTool("list_color_samplers", "Lists the active color samplers.", {}, "var a=[];var cs=_doc().colorSamplers;for(var i=0;i<cs.length;i++){try{a.push({x:Math.round(cs[i].position[0].as('px')),y:Math.round(cs[i].position[1].as('px')),color:('#'+cs[i].color.rgb.hexValue).toUpperCase()});}catch(e){}}return a;");
689
689
 
690
690
  /* ====================== BATCH ====================== */
691
- jsxTool("batch_rename_layers", "Renombra capas en masa (find/replace y/o prefix/suffix), recursivo.", { find: S.string().optional(), replace: S.string().optional(), prefix: S.string().optional(), suffix: S.string().optional() },
691
+ jsxTool("batch_rename_layers", "Bulk-renames layers (find/replace and/or prefix/suffix), recursive.", { find: S.string().optional(), replace: S.string().optional(), prefix: S.string().optional(), suffix: S.string().optional() },
692
692
  "function walk(ls,fn){for(var i=0;i<ls.length;i++){fn(ls[i]);if(ls[i].typename==='LayerSet')walk(ls[i].layers,fn);}}var n=0;walk(_doc().layers,function(L){var nm=L.name;if(A.find!=null&&A.find!=='')nm=nm.split(A.find).join(A.replace||'');if(A.prefix)nm=A.prefix+nm;if(A.suffix)nm=nm+A.suffix;if(nm!==L.name){L.name=nm;n++;}});return {renamed:n};");
693
- jsxTool("batch_set_visibility", "Muestra/oculta varias capas por nombre.", { names: S.array(S.string()), visible: S.boolean() },
693
+ jsxTool("batch_set_visibility", "Shows/hides several layers by name.", { names: S.array(S.string()), visible: S.boolean() },
694
694
  "var n=0;for(var i=0;i<A.names.length;i++){try{_need(A.names[i]).visible=!!A.visible;n++;}catch(e){}}return {changed:n};");
695
- jsxTool("batch_translate_layers", "Mueve varias capas (dx,dy px).", { names: S.array(S.string()), dx: S.number(), dy: S.number() },
695
+ jsxTool("batch_translate_layers", "Moves several layers (dx,dy px).", { names: S.array(S.string()), dx: S.number(), dy: S.number() },
696
696
  "var n=0;for(var i=0;i<A.names.length;i++){try{_need(A.names[i]).translate(A.dx||0,A.dy||0);n++;}catch(e){}}return {moved:n};");
697
- jsxTool("batch_export_layers_png", "Exporta varias capas/carpetas aisladas a PNG en una carpeta (trim opcional). Devuelve rutas.", { names: S.array(S.string()), folder: S.string(), trim: S.boolean().optional() },
697
+ jsxTool("batch_export_layers_png", "Exports several isolated layers/folders to PNG into a folder (optional trim). Returns paths.", { names: S.array(S.string()), folder: S.string(), trim: S.boolean().optional() },
698
698
  "var res=[];function hideAll(ls){for(var j=0;j<ls.length;j++){try{ls[j].visible=false;}catch(e){}if(ls[j].typename==='LayerSet')hideAll(ls[j].layers);}}function show(n){try{n.visible=true;}catch(e){}if(n.typename==='LayerSet')for(var j=0;j<n.layers.length;j++)show(n.layers[j]);}var orig=_doc();for(var i=0;i<A.names.length;i++){var work=orig.duplicate();app.activeDocument=work;var L=_find(A.names[i]);if(!L){work.close(SaveOptions.DONOTSAVECHANGES);res.push({name:A.names[i],ok:false});app.activeDocument=orig;continue;}hideAll(work.layers);show(L);var p=L.parent;while(p&&p.typename==='LayerSet'){try{p.visible=true;}catch(e){}p=p.parent;}if(A.trim!==false){try{work.trim(TrimType.TRANSPARENT,true,true,true,true);}catch(e){}}var slug=String(A.names[i]).replace(/[^a-zA-Z0-9_\\-]+/g,'_');var f=new File(A.folder+'/'+slug+'.png');var o=new PNGSaveOptions();o.compression=6;o.interlaced=false;work.saveAs(f,o,true,Extension.LOWERCASE);work.close(SaveOptions.DONOTSAVECHANGES);app.activeDocument=orig;res.push({name:A.names[i],ok:true,path:f.fsName});}return res;", 600000);
699
699
 
700
700
  /* ====================== GUÍAS (más) ====================== */
701
- jsxTool("delete_guide", "Elimina una guía por índice (ver list_guides).", { index: S.number() }, "_doc().guides[A.index].remove();return 'ok';");
702
- jsxTool("add_guides_grid", "Agrega guías en fracciones del lienzo (ej. cols=3, rows=3 => tercios).", { cols: S.number().optional(), rows: S.number().optional() },
701
+ jsxTool("delete_guide", "Deletes a guide by index (see list_guides).", { index: S.number() }, "_doc().guides[A.index].remove();return 'ok';");
702
+ jsxTool("add_guides_grid", "Adds guides at fractions of the canvas (e.g. cols=3, rows=3 => thirds).", { cols: S.number().optional(), rows: S.number().optional() },
703
703
  "var d=_doc();var w=d.width.as?d.width.as('px'):Math.round(d.width);var h=d.height.as?d.height.as('px'):Math.round(d.height);var c=A.cols||0,r=A.rows||0;for(var i=1;i<c;i++)d.guides.add(Direction.VERTICAL,UnitValue(w*i/c,'px'));for(var j=1;j<r;j++)d.guides.add(Direction.HORIZONTAL,UnitValue(h*j/r,'px'));return 'ok';");
704
704
 
705
705
  /* ====================== LAYER COMPS ====================== */
706
- jsxTool("list_layer_comps", "Lista los layer comps del documento.", {}, "var a=[];var lc=_doc().layerComps;for(var i=0;i<lc.length;i++)a.push({name:lc[i].name,comment:lc[i].comment,selected:lc[i].selected});return a;");
707
- jsxTool("create_layer_comp", "Crea un layer comp con el estado actual.", { name: S.string(), comment: S.string().optional(), appearance: S.boolean().optional(), position: S.boolean().optional(), visibility: S.boolean().optional() },
706
+ jsxTool("list_layer_comps", "Lists the document's layer comps.", {}, "var a=[];var lc=_doc().layerComps;for(var i=0;i<lc.length;i++)a.push({name:lc[i].name,comment:lc[i].comment,selected:lc[i].selected});return a;");
707
+ jsxTool("create_layer_comp", "Creates a layer comp with the current state.", { name: S.string(), comment: S.string().optional(), appearance: S.boolean().optional(), position: S.boolean().optional(), visibility: S.boolean().optional() },
708
708
  "var c=_doc().layerComps.add(A.name,A.comment||'',A.appearance!==false,A.position!==false,A.visibility!==false);return 'comp: '+c.name;");
709
- jsxTool("apply_layer_comp", "Aplica un layer comp por nombre.", { name: S.string() }, "_doc().layerComps.getByName(A.name).apply();return 'ok';");
710
- jsxTool("update_layer_comp", "Actualiza un layer comp con el estado actual.", { name: S.string() }, "_doc().layerComps.getByName(A.name).recapture();return 'ok';");
711
- jsxTool("delete_layer_comp", "Elimina un layer comp por nombre.", { name: S.string() }, "_doc().layerComps.getByName(A.name).remove();return 'ok';");
712
- jsxTool("restore_last_document_state", "Restaura el último estado documentado (antes de aplicar comps).", {}, "_doc().layerComps.restoreLastDocumentState();return 'ok';");
709
+ jsxTool("apply_layer_comp", "Applies a layer comp by name.", { name: S.string() }, "_doc().layerComps.getByName(A.name).apply();return 'ok';");
710
+ jsxTool("update_layer_comp", "Updates a layer comp with the current state.", { name: S.string() }, "_doc().layerComps.getByName(A.name).recapture();return 'ok';");
711
+ jsxTool("delete_layer_comp", "Deletes a layer comp by name.", { name: S.string() }, "_doc().layerComps.getByName(A.name).remove();return 'ok';");
712
+ jsxTool("restore_last_document_state", "Restores the last documented state (before applying comps).", {}, "_doc().layerComps.restoreLastDocumentState();return 'ok';");
713
713
 
714
714
  /* ====================== ACCIONES (gestión completa) ====================== */
715
- jsxTool("list_action_sets", "Lista los sets de acciones y sus acciones.", {}, "var a=[];var r=new ActionReference();r.putEnumerated(charIDToTypeID('ASet'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var cnt=0;try{cnt=executeActionGet(r).getInteger(charIDToTypeID('NmbC'));}catch(e){}for(var i=1;i<=cnt;i++){try{var rr=new ActionReference();rr.putIndex(charIDToTypeID('ASet'),i);var d=executeActionGet(rr);var setName=d.getString(charIDToTypeID('Nm '));var actions=[];var nAct=d.hasKey(charIDToTypeID('NmbC'))?d.getInteger(charIDToTypeID('NmbC')):0;for(var j=1;j<=nAct;j++){try{var rr2=new ActionReference();rr2.putIndex(charIDToTypeID('Actn'),j);rr2.putIndex(charIDToTypeID('ASet'),i);var d2=executeActionGet(rr2);actions.push(d2.getString(charIDToTypeID('Nm ')));}catch(e2){}}a.push({set:setName,actions:actions});}catch(e){}}return a;");
716
- jsxTool("load_action_set", "Carga un set de acciones desde un archivo .atn.", { path: S.string() }, "app.load(new File(A.path));return 'ok';");
717
- jsxTool("play_action_step", "Reproduce un paso específico de una acción (por índice) sin correrla entera.", { set: S.string(), action: S.string(), step: S.number().optional() },
715
+ jsxTool("list_action_sets", "Lists action sets and their actions.", {}, "var a=[];var r=new ActionReference();r.putEnumerated(charIDToTypeID('ASet'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));var cnt=0;try{cnt=executeActionGet(r).getInteger(charIDToTypeID('NmbC'));}catch(e){}for(var i=1;i<=cnt;i++){try{var rr=new ActionReference();rr.putIndex(charIDToTypeID('ASet'),i);var d=executeActionGet(rr);var setName=d.getString(charIDToTypeID('Nm '));var actions=[];var nAct=d.hasKey(charIDToTypeID('NmbC'))?d.getInteger(charIDToTypeID('NmbC')):0;for(var j=1;j<=nAct;j++){try{var rr2=new ActionReference();rr2.putIndex(charIDToTypeID('Actn'),j);rr2.putIndex(charIDToTypeID('ASet'),i);var d2=executeActionGet(rr2);actions.push(d2.getString(charIDToTypeID('Nm ')));}catch(e2){}}a.push({set:setName,actions:actions});}catch(e){}}return a;");
716
+ jsxTool("load_action_set", "Loads an action set from a .atn file.", { path: S.string() }, "app.load(new File(A.path));return 'ok';");
717
+ jsxTool("play_action_step", "Plays a specific step of an action (by index) without running the whole thing.", { set: S.string(), action: S.string(), step: S.number().optional() },
718
718
  "app.doAction(A.action,A.set);return 'ok';");
719
- jsxTool("delete_action", "Elimina una acción de un set (por Action Manager).", { set: S.string(), action: S.string() },
719
+ jsxTool("delete_action", "Deletes an action from a set (via Action Manager).", { set: S.string(), action: S.string() },
720
720
  "var d=new ActionDescriptor();var r=new ActionReference();r.putName(charIDToTypeID('Actn'),A.action);r.putName(charIDToTypeID('ASet'),A.set);d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Dlt '),d,DialogModes.NO);return 'ok';");
721
- jsxTool("delete_action_set", "[NO VERIFICADO — falló en pruebas en vivo, 'comando Eliminar no disponible'] Elimina un set de acciones completo. Si falla, borralo manualmente en el panel Acciones.", { set: S.string() },
721
+ jsxTool("delete_action_set", "[UNVERIFIED — failed in live testing, 'Delete command unavailable'] Deletes an entire action set. If it fails, delete it manually in the Actions panel.", { set: S.string() },
722
722
  "var d=new ActionDescriptor();var r=new ActionReference();r.putName(charIDToTypeID('ASet'),A.set);d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Dlt '),d,DialogModes.NO);return 'ok';");
723
- jsxTool("create_action_set", "[NO VERIFICADO — falló en pruebas en vivo, 2 variantes de descriptor probadas, ambas 'comando Crear no disponible'] Crea un set de acciones nuevo (vacío). Si falla, crealo manualmente en el panel Acciones.", { name: S.string() },
723
+ jsxTool("create_action_set", "[UNVERIFIED — failed in live testing, 2 descriptor variants tried, both 'Create command unavailable'] Creates a new (empty) action set. If it fails, create it manually in the Actions panel.", { name: S.string() },
724
724
  "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(charIDToTypeID('ASet'));d.putReference(charIDToTypeID('null'),r);d.putString(charIDToTypeID('Nm '),A.name);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'set: '+A.name;");
725
- jsxTool("record_action_start", "Inicia grabación de una acción nueva dentro de un set (grabación real de la sesión de usuario, no de comandos scripteados).", { set: S.string(), name: S.string() },
726
- "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(charIDToTypeID('Actn'));d.putReference(charIDToTypeID('null'),r);d.putString(charIDToTypeID('Nm '),A.name);var r2=new ActionReference();r2.putName(charIDToTypeID('ASet'),A.set);d.putReference(charIDToTypeID('From'),r2);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'grabando: '+A.name;");
725
+ jsxTool("record_action_start", "Starts recording a new action inside a set (real recording of the user's session, not of scripted commands).", { set: S.string(), name: S.string() },
726
+ "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(charIDToTypeID('Actn'));d.putReference(charIDToTypeID('null'),r);d.putString(charIDToTypeID('Nm '),A.name);var r2=new ActionReference();r2.putName(charIDToTypeID('ASet'),A.set);d.putReference(charIDToTypeID('From'),r2);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'recording: '+A.name;");
727
727
 
728
728
  /* ====================== COLOR SETTINGS / PERFILES ====================== */
729
- jsxTool("get_color_profile", "Perfil de color asignado al documento.", {}, "return {profile:_doc().colorProfileName, type:String(_doc().colorProfileType).replace('ColorProfile.','')};");
730
- jsxTool("assign_color_profile", "Asigna un perfil de color (por nombre, ej. 'sRGB IEC61966-2.1') sin convertir píxeles.", { profile: S.string() }, "_doc().colorProfileType=ColorProfile.CUSTOM;_doc().colorProfileName=A.profile;return 'ok';");
731
- jsxTool("convert_to_profile", "Convierte el documento a un perfil de color (Edit > Convert to Profile).", { profile: S.string(), intent: S.string().optional() },
729
+ jsxTool("get_color_profile", "Color profile assigned to the document.", {}, "return {profile:_doc().colorProfileName, type:String(_doc().colorProfileType).replace('ColorProfile.','')};");
730
+ jsxTool("assign_color_profile", "Assigns a color profile (by name, e.g. 'sRGB IEC61966-2.1') without converting pixels.", { profile: S.string() }, "_doc().colorProfileType=ColorProfile.CUSTOM;_doc().colorProfileName=A.profile;return 'ok';");
731
+ jsxTool("convert_to_profile", "Converts the document to a color profile (Edit > Convert to Profile).", { profile: S.string(), intent: S.string().optional() },
732
732
  "var d=new ActionDescriptor();var dest=new ActionDescriptor();dest.putString(stringIDToTypeID('profile'),A.profile);d.putObject(stringIDToTypeID('destinationProfile'),stringIDToTypeID('ICCProfile'),dest);d.putEnumerated(charIDToTypeID('Intn'),charIDToTypeID('Intn'),charIDToTypeID(A.intent||'Rltv'));executeAction(stringIDToTypeID('convertToProfile'),d,DialogModes.NO);return 'ok';");
733
- jsxTool("remove_color_profile", "Quita el perfil de color del documento (untagged).", {}, "_doc().colorProfileType=ColorProfile.NONE;return 'ok';");
733
+ jsxTool("remove_color_profile", "Removes the document's color profile (untagged).", {}, "_doc().colorProfileType=ColorProfile.NONE;return 'ok';");
734
734
 
735
735
  /* ====================== PRESETS DE DOCUMENTO / PRINT ====================== */
736
- jsxTool("create_document_from_preset", "Crea un documento desde un preset guardado (por nombre).", { preset: S.string(), name: S.string().optional() },
737
- "var d=new ActionDescriptor();d.putString(stringIDToTypeID('preset'),A.preset);if(A.name)d.putString(charIDToTypeID('Nm '),A.name);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'creado desde preset: '+A.preset;");
738
- jsxTool("print_document", "Imprime el documento activo con la config de impresión guardada (usa el diálogo de impresión por defecto de PS).", {},
739
- "_doc().print();return 'enviado a impresión';");
740
- jsxTool("print_one_copy", "Imprime una copia directa sin diálogo.", {}, "app.doAction('Default Actions','Print One Copy');return 'ok';");
736
+ jsxTool("create_document_from_preset", "Creates a document from a saved preset (by name).", { preset: S.string(), name: S.string().optional() },
737
+ "var d=new ActionDescriptor();d.putString(stringIDToTypeID('preset'),A.preset);if(A.name)d.putString(charIDToTypeID('Nm '),A.name);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'created from preset: '+A.preset;");
738
+ jsxTool("print_document", "Prints the active document with the saved print config (uses PS's default print dialog).", {},
739
+ "_doc().print();return 'sent to print';");
740
+ jsxTool("print_one_copy", "Prints one copy directly, no dialog.", {}, "app.doAction('Default Actions','Print One Copy');return 'ok';");
741
741
 
742
742
  /* ====================== EDICIÓN CONSCIENTE DEL CONTENIDO / RECOMPOSICIÓN ====================== */
743
- jsxTool("content_aware_fill", "Rellena la selección actual usando Relleno según el contenido (config por defecto).", {},
743
+ jsxTool("content_aware_fill", "Fills the current selection using Content-Aware Fill (default config).", {},
744
744
  "var d=new ActionDescriptor();d.putEnumerated(stringIDToTypeID('using'),stringIDToTypeID('fillContents'),stringIDToTypeID('contentAware'));d.putUnitDouble(charIDToTypeID('Opct'),charIDToTypeID('#Prc'),100);d.putEnumerated(charIDToTypeID('Md '),charIDToTypeID('BlnM'),charIDToTypeID('Nrml'));executeAction(charIDToTypeID('Fl '),d,DialogModes.NO);return 'ok';");
745
- jsxTool("content_aware_scale", "[NO VERIFICADO — falló en pruebas en vivo con 'comando desconocido'] Intento de escala consciente del contenido. Content-Aware Scale no tiene un evento de Action Manager simple y documentado (a diferencia de Content-Aware Fill, que sí funciona); probablemente requiera una secuencia de Free Transform con flags especiales no confirmados. Usalo con escepticismo, o hacelo manualmente (Edit > Content-Aware Scale).", { widthPercent: S.number().optional(), heightPercent: S.number().optional(), name: S.string().optional() },
745
+ jsxTool("content_aware_scale", "[UNVERIFIED — failed in live testing with 'unknown command'] Attempt at content-aware scaling. Content-Aware Scale doesn't have a simple, documented Action Manager event (unlike Content-Aware Fill, which does work); it likely requires a Free Transform sequence with special flags that aren't confirmed. Use with skepticism, or do it manually (Edit > Content-Aware Scale).", { widthPercent: S.number().optional(), heightPercent: S.number().optional(), name: S.string().optional() },
746
746
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('Wdth'),charIDToTypeID('#Prc'),A.widthPercent||100);d.putUnitDouble(charIDToTypeID('Hght'),charIDToTypeID('#Prc'),A.heightPercent||100);executeAction(stringIDToTypeID('contentAwareScale'),d,DialogModes.NO);return 'ok';");
747
- jsxTool("content_aware_move", "[NO VERIFICADO — misma familia que content_aware_scale, mismo riesgo de ID incorrecto] Intento de mover contenido consciente del contenido (dx,dy px). Hacelo manualmente (herramienta Content-Aware Move) si esto falla.", { dx: S.number(), dy: S.number() },
747
+ jsxTool("content_aware_move", "[UNVERIFIED — same family as content_aware_scale, same risk of a wrong ID] Attempt at content-aware move (dx,dy px). Do it manually (Content-Aware Move tool) if this fails.", { dx: S.number(), dy: S.number() },
748
748
  "var d=new ActionDescriptor();d.putUnitDouble(stringIDToTypeID('horizontal'),charIDToTypeID('#Pxl'),A.dx);d.putUnitDouble(stringIDToTypeID('vertical'),charIDToTypeID('#Pxl'),A.dy);executeAction(stringIDToTypeID('contentAwareMove'),d,DialogModes.NO);return 'ok';");
749
- jsxTool("perspective_warp", "Activa/commitea Perspective Warp sobre la capa (best-effort, requiere UI en algunos flujos).", { name: S.string().optional() },
749
+ jsxTool("perspective_warp", "Activates/commits Perspective Warp on the layer (best-effort, requires UI in some flows).", { name: S.string().optional() },
750
750
  "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('perspectiveWarp'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
751
- jsxTool("puppet_warp_commit", "Confirma una edición de Puppet Warp en curso.", {}, "executeAction(stringIDToTypeID('puppetWarp'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
752
- jsxTool("auto_align_layers", "Alinea automáticamente las capas seleccionadas (mode: auto|perspective|cylindrical|spherical|collage|reposition).", { mode: S.string().optional() },
751
+ jsxTool("puppet_warp_commit", "Commits an in-progress Puppet Warp edit.", {}, "executeAction(stringIDToTypeID('puppetWarp'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
752
+ jsxTool("auto_align_layers", "Automatically aligns the selected layers (mode: auto|perspective|cylindrical|spherical|collage|reposition).", { mode: S.string().optional() },
753
753
  "var m={auto:stringIDToTypeID('auto'),perspective:stringIDToTypeID('perspective'),cylindrical:stringIDToTypeID('cylindrical'),spherical:stringIDToTypeID('spherical'),collage:stringIDToTypeID('collage'),reposition:stringIDToTypeID('reposition')};var d=new ActionDescriptor();d.putEnumerated(stringIDToTypeID('alignLayer'),stringIDToTypeID('alignLayer'),m[A.mode||'auto']);executeAction(stringIDToTypeID('align'),d,DialogModes.NO);return 'ok';");
754
- jsxTool("auto_blend_layers", "Fusiona automáticamente las capas seleccionadas (method: panorama|stack|auto).", { method: S.string().optional(), seamlessTones: S.boolean().optional() },
754
+ jsxTool("auto_blend_layers", "Automatically blends the selected layers (method: panorama|stack|auto).", { method: S.string().optional(), seamlessTones: S.boolean().optional() },
755
755
  "var m={panorama:stringIDToTypeID('panorama'),stack:stringIDToTypeID('stackImages'),auto:stringIDToTypeID('auto')};var d=new ActionDescriptor();d.putEnumerated(stringIDToTypeID('blendMethod'),stringIDToTypeID('blendMethod'),m[A.method||'auto']);d.putBoolean(stringIDToTypeID('seamlessTones'),A.seamlessTones!==false);executeAction(stringIDToTypeID('autoBlend'),d,DialogModes.NO);return 'ok';");
756
- jsxTool("merge_to_hdr", "Combina archivos en HDR (paths de imágenes).", { paths: S.array(S.string()) },
756
+ jsxTool("merge_to_hdr", "Merges files into HDR (image paths).", { paths: S.array(S.string()) },
757
757
  "var list=new ActionList();for(var i=0;i<A.paths.length;i++){var dd=new ActionDescriptor();dd.putPath(charIDToTypeID('null'),new File(A.paths[i]));list.putObject(charIDToTypeID('null'),dd);}var d=new ActionDescriptor();d.putList(charIDToTypeID('null'),list);executeAction(stringIDToTypeID('mergeToHDR'),d,DialogModes.NO);return 'ok';", 300000);
758
- jsxTool("load_files_into_stack", "Carga archivos como capas de un mismo documento (Scripts > Load Files into Stack), opcionalmente alineados.", { paths: S.array(S.string()), align: S.boolean().optional(), createSmartObject: S.boolean().optional() },
758
+ jsxTool("load_files_into_stack", "Loads files as layers of a single document (Scripts > Load Files into Stack), optionally aligned.", { paths: S.array(S.string()), align: S.boolean().optional(), createSmartObject: S.boolean().optional() },
759
759
  "var list=new ActionList();for(var i=0;i<A.paths.length;i++){var dd=new ActionDescriptor();dd.putPath(charIDToTypeID('null'),new File(A.paths[i]));list.putObject(charIDToTypeID('null'),dd);}var d=new ActionDescriptor();d.putList(stringIDToTypeID('using'),list);if(A.createSmartObject)d.putBoolean(stringIDToTypeID('linked'),false);executeAction(stringIDToTypeID('loadIntoStack'),d,DialogModes.NO);if(A.align){try{executeAction(stringIDToTypeID('align'),new ActionDescriptor(),DialogModes.NO);}catch(e){}}return 'ok';", 300000);
760
760
 
761
761
  /* ====================== SELECT & MASK / REFINE EDGE ====================== */
762
- jsxTool("refine_selection_edge", "Refina el borde de la selección actual (Select and Mask, parámetros básicos).", { smooth: S.number().optional(), feather: S.number().optional(), contrast: S.number().optional(), shiftEdge: S.number().optional() },
762
+ jsxTool("refine_selection_edge", "Refines the current selection's edge (Select and Mask, basic parameters).", { smooth: S.number().optional(), feather: S.number().optional(), contrast: S.number().optional(), shiftEdge: S.number().optional() },
763
763
  "var d=new ActionDescriptor();d.putInteger(stringIDToTypeID('smoothness'),A.smooth||0);d.putUnitDouble(stringIDToTypeID('featherRadius'),charIDToTypeID('#Pxl'),A.feather||0);d.putUnitDouble(stringIDToTypeID('contrast'),charIDToTypeID('#Prc'),A.contrast||0);d.putUnitDouble(stringIDToTypeID('shiftEdge'),charIDToTypeID('#Prc'),A.shiftEdge||0);executeAction(stringIDToTypeID('refineSelectionEdge'),d,DialogModes.NO);return 'ok';");
764
764
 
765
765
  /* ====================== SLICES ====================== */
@@ -767,19 +767,19 @@ jsxTool("refine_selection_edge", "Refina el borde de la selección actual (Selec
767
767
  en el DOM de ExtendScript (verificado en vivo: typeof ambos da 'undefined'). Adobe removió
768
768
  la API de scripting de Slices de versiones modernas de Photoshop. Estas 4 tools quedan
769
769
  documentadas como NO FUNCIONALES — no hay alternativa de Action Manager conocida. */
770
- jsxTool("list_slices", "[NO FUNCIONAL en Photoshop moderno — API de Slices removida, ver nota arriba] Lista los slices del documento.", {}, "var a=[];function walk(s){for(var i=0;i<s.length;i++){var b=s[i].bounds;a.push({name:s[i].name,bounds:[Math.round(b[0]),Math.round(b[1]),Math.round(b[2]),Math.round(b[3])]});}}walk(_doc().slices);return a;");
771
- jsxTool("add_slice", "[NO FUNCIONAL en Photoshop moderno — API de Slices removida] Agrega un slice (px).", { name: S.string(), x: S.number(), y: S.number(), width: S.number(), height: S.number() },
770
+ jsxTool("list_slices", "[NOT FUNCTIONAL in modern Photoshop — Slices API removed, see note above] Lists the document's slices.", {}, "var a=[];function walk(s){for(var i=0;i<s.length;i++){var b=s[i].bounds;a.push({name:s[i].name,bounds:[Math.round(b[0]),Math.round(b[1]),Math.round(b[2]),Math.round(b[3])]});}}walk(_doc().slices);return a;");
771
+ jsxTool("add_slice", "[NOT FUNCTIONAL in modern Photoshop — Slices API removed] Adds a slice (px).", { name: S.string(), x: S.number(), y: S.number(), width: S.number(), height: S.number() },
772
772
  "var s=_doc().slices.add(A.name,SliceType.USER,A.x,A.y,A.x+A.width,A.y+A.height);return 'slice: '+s.name;");
773
- jsxTool("delete_slice", "[NO FUNCIONAL en Photoshop moderno — API de Slices removida] Elimina un slice por nombre.", { name: S.string() }, "function find(s){for(var i=0;i<s.length;i++)if(s[i].name===A.name)return s[i];return null;}var s=find(_doc().slices);if(!s)throw 'no encontrado';s.remove();return 'ok';");
774
- jsxTool("export_slices", "[NO FUNCIONAL en Photoshop moderno — API de Slices removida] Exporta todos los slices como imágenes a una carpeta.", { folder: S.string() },
773
+ jsxTool("delete_slice", "[NOT FUNCTIONAL in modern Photoshop — Slices API removed] Deletes a slice by name.", { name: S.string() }, "function find(s){for(var i=0;i<s.length;i++)if(s[i].name===A.name)return s[i];return null;}var s=find(_doc().slices);if(!s)throw 'not found';s.remove();return 'ok';");
774
+ jsxTool("export_slices", "[NOT FUNCTIONAL in modern Photoshop — Slices API removed] Exports all slices as images to a folder.", { folder: S.string() },
775
775
  "var o=new ExportOptionsSaveForWeb();o.format=SaveDocumentType.PNG;_doc().exportDocument(new Folder(A.folder),ExportType.SAVEFORWEB,o);return {folder:A.folder};");
776
776
 
777
777
  /* ====================== ARTBOARDS ====================== */
778
- jsxTool("create_artboard", "Crea un artboard (mesa de trabajo) nuevo.", { name: S.string().optional(), x: S.number().optional(), y: S.number().optional(), width: S.number(), height: S.number() },
779
- "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('artboardSection'));d.putReference(charIDToTypeID('null'),r);var art=new ActionDescriptor();var b=new ActionDescriptor();b.putDouble(charIDToTypeID('Top '),A.y||0);b.putDouble(charIDToTypeID('Left'),A.x||0);b.putDouble(charIDToTypeID('Btom'),(A.y||0)+A.height);b.putDouble(charIDToTypeID('Rght'),(A.x||0)+A.width);art.putObject(stringIDToTypeID('artboardRect'),charIDToTypeID('Rctn'),b);d.putObject(stringIDToTypeID('artboard'),stringIDToTypeID('artboard'),art);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);if(A.name){try{_doc().activeLayer.name=A.name;}catch(e){}}return 'artboard creado';");
780
- jsxTool("resize_artboard", "Redimensiona el artboard/grupo nombrado.", { name: S.string(), width: S.number(), height: S.number() },
778
+ jsxTool("create_artboard", "Creates a new artboard.", { name: S.string().optional(), x: S.number().optional(), y: S.number().optional(), width: S.number(), height: S.number() },
779
+ "var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('artboardSection'));d.putReference(charIDToTypeID('null'),r);var art=new ActionDescriptor();var b=new ActionDescriptor();b.putDouble(charIDToTypeID('Top '),A.y||0);b.putDouble(charIDToTypeID('Left'),A.x||0);b.putDouble(charIDToTypeID('Btom'),(A.y||0)+A.height);b.putDouble(charIDToTypeID('Rght'),(A.x||0)+A.width);art.putObject(stringIDToTypeID('artboardRect'),charIDToTypeID('Rctn'),b);d.putObject(stringIDToTypeID('artboard'),stringIDToTypeID('artboard'),art);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);if(A.name){try{_doc().activeLayer.name=A.name;}catch(e){}}return 'artboard created';");
780
+ jsxTool("resize_artboard", "Resizes the named artboard/group.", { name: S.string(), width: S.number(), height: S.number() },
781
781
  "_sel(_need(A.name));var d=new ActionDescriptor();var b=new ActionDescriptor();b.putDouble(charIDToTypeID('Wdth'),A.width);b.putDouble(charIDToTypeID('Hght'),A.height);d.putObject(stringIDToTypeID('artboardRect'),charIDToTypeID('Rctn'),b);var r=_layerRef();d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok';");
782
- jsxTool("list_artboards", "Lista los grupos marcados como artboard.", {}, "var a=[];function walk(ls){for(var i=0;i<ls.length;i++){var L=ls[i];if(L.typename==='LayerSet'){try{_sel(L);var g=executeActionGet(_layerRef());if(g.hasKey(stringIDToTypeID('artboard')))a.push({name:L.name});}catch(e){}walk(L.layers);}}}walk(_doc().layers);return a;");
782
+ jsxTool("list_artboards", "Lists groups marked as artboards.", {}, "var a=[];function walk(ls){for(var i=0;i<ls.length;i++){var L=ls[i];if(L.typename==='LayerSet'){try{_sel(L);var g=executeActionGet(_layerRef());if(g.hasKey(stringIDToTypeID('artboard')))a.push({name:L.name});}catch(e){}walk(L.layers);}}}walk(_doc().layers);return a;");
783
783
 
784
784
  /* ====================== VARIABLES / DATA SETS (gráficos con datos) ====================== */
785
785
  /* NO VERIFICADO / probablemente removido en Photoshop moderno: en pruebas en vivo (27.9.1),
@@ -787,306 +787,306 @@ jsxTool("list_artboards", "Lista los grupos marcados como artboard.", {}, "var a
787
787
  consistente con que Adobe deprecó/retiró Variables (heredado de ImageReady) del scripting
788
788
  moderno. Dejamos las tools por si en alguna build/flujo específico todavía funcionan, pero
789
789
  no confíes en ellas sin probar primero. */
790
- jsxTool("define_text_variable", "[NO VERIFICADO — Data Sets puede estar removido en Photoshop moderno, ver nota arriba] Define una capa de texto como variable de tipo texto (para Data Sets).", { name: S.string() },
790
+ jsxTool("define_text_variable", "[UNVERIFIED — Data Sets may be removed in modern Photoshop, see note above] Defines a text layer as a text-type variable (for Data Sets).", { name: S.string() },
791
791
  "_sel(_need(A.name));var d=new ActionDescriptor();var v=new ActionDescriptor();v.putString(charIDToTypeID('Nm '),A.name);v.putEnumerated(stringIDToTypeID('variableType'),stringIDToTypeID('variableType'),stringIDToTypeID('textReplace'));var r=_layerRef();d.putReference(charIDToTypeID('null'),r);d.putObject(stringIDToTypeID('variable'),stringIDToTypeID('variable'),v);executeAction(stringIDToTypeID('assignDataDrivenLayer'),d,DialogModes.NO);return 'ok';");
792
- jsxTool("define_visibility_variable", "[NO VERIFICADO — ver nota de Data Sets arriba] Define una capa como variable de visibilidad (para Data Sets).", { name: S.string() },
792
+ jsxTool("define_visibility_variable", "[UNVERIFIED — see Data Sets note above] Defines a layer as a visibility variable (for Data Sets).", { name: S.string() },
793
793
  "_sel(_need(A.name));var d=new ActionDescriptor();var v=new ActionDescriptor();v.putString(charIDToTypeID('Nm '),A.name);v.putEnumerated(stringIDToTypeID('variableType'),stringIDToTypeID('variableType'),stringIDToTypeID('visibility'));var r=_layerRef();d.putReference(charIDToTypeID('null'),r);d.putObject(stringIDToTypeID('variable'),stringIDToTypeID('variable'),v);executeAction(stringIDToTypeID('assignDataDrivenLayer'),d,DialogModes.NO);return 'ok';");
794
- jsxTool("import_data_sets_from_csv", "Importa Data Sets desde un CSV/TXT delimitado por tabs (formato nativo de PS).", { path: S.string() },
794
+ jsxTool("import_data_sets_from_csv", "Imports Data Sets from a tab-delimited CSV/TXT (PS's native format).", { path: S.string() },
795
795
  "var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));executeAction(stringIDToTypeID('importDataSets'),d,DialogModes.NO);return 'ok';");
796
- jsxTool("apply_data_set", "Aplica un Data Set por índice/nombre.", { name: S.string() },
796
+ jsxTool("apply_data_set", "Applies a Data Set by index/name.", { name: S.string() },
797
797
  "var d=new ActionDescriptor();d.putString(stringIDToTypeID('name'),A.name);executeAction(stringIDToTypeID('applyDataSet'),d,DialogModes.NO);return 'ok';");
798
- jsxTool("export_data_sets_as_files", "Renderiza cada Data Set a un archivo (batch de variables, formato por defecto PSD/PNG según flujo activo).", { folder: S.string(), fileType: S.string().optional() },
798
+ jsxTool("export_data_sets_as_files", "Renders each Data Set to a file (variable batch, default format PSD/PNG depending on the active flow).", { folder: S.string(), fileType: S.string().optional() },
799
799
  "var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new Folder(A.folder));d.putString(stringIDToTypeID('fileNamePrefix'),'ds_');executeAction(stringIDToTypeID('exportDataSetsAsFiles'),d,DialogModes.NO);return {folder:A.folder};", 300000);
800
800
 
801
801
  /* ====================== 3D (básico) ====================== */
802
- jsxTool("new_3d_layer_from_file", "Crea una capa 3D nueva desde un archivo de modelo.", { path: S.string() },
803
- "app.open(new File(A.path));return 'abierto como 3D: '+app.activeDocument.name;");
804
- jsxTool("render_3d_layer", "Renderiza la capa 3D activa/nombrada.", { name: S.string().optional() },
802
+ jsxTool("new_3d_layer_from_file", "Creates a new 3D layer from a model file.", { path: S.string() },
803
+ "app.open(new File(A.path));return 'opened as 3D: '+app.activeDocument.name;");
804
+ jsxTool("render_3d_layer", "Renders the active/named 3D layer.", { name: S.string().optional() },
805
805
  "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('render3D'),new ActionDescriptor(),DialogModes.NO);return 'ok';", 300000);
806
- jsxTool("merge_3d_layer", "Aplana (rasteriza) una capa 3D a 2D.", { name: S.string().optional() },
806
+ jsxTool("merge_3d_layer", "Flattens (rasterizes) a 3D layer to 2D.", { name: S.string().optional() },
807
807
  "if(A.name)_sel(_need(A.name));_doc().activeLayer.rasterize(RasterizeType.ENTIRELAYER);return 'ok';");
808
808
 
809
809
  /* ====================== VIDEO / TIMELINE (capas de video) ====================== */
810
- jsxTool("set_timeline_current_time", "Mueve el playhead del panel Timeline (segundos).", { seconds: S.number() },
810
+ jsxTool("set_timeline_current_time", "Moves the Timeline panel's playhead (seconds).", { seconds: S.number() },
811
811
  "var d=new ActionDescriptor();d.putUnitDouble(stringIDToTypeID('time'),stringIDToTypeID('seconds'),A.seconds);executeAction(stringIDToTypeID('setCurrentTime'),d,DialogModes.NO);return 'ok';");
812
- jsxTool("add_video_keyframe", "Agrega un keyframe de video en el tiempo actual para una propiedad (position|opacity|style).", { name: S.string(), property: S.string().optional() },
812
+ jsxTool("add_video_keyframe", "Adds a video keyframe at the current time for a property (position|opacity|style).", { name: S.string(), property: S.string().optional() },
813
813
  "_sel(_need(A.name));var d=new ActionDescriptor();d.putEnumerated(stringIDToTypeID('videoTimeProperty'),stringIDToTypeID('videoTimeProperty'),stringIDToTypeID(A.property||'position'));executeAction(stringIDToTypeID('addVideoKeyframe'),d,DialogModes.NO);return 'ok';");
814
- jsxTool("set_video_duration", "Define la duración del documento de video (segundos).", { seconds: S.number() },
814
+ jsxTool("set_video_duration", "Sets the video document's duration (seconds).", { seconds: S.number() },
815
815
  "var d=new ActionDescriptor();d.putUnitDouble(stringIDToTypeID('duration'),stringIDToTypeID('seconds'),A.seconds);executeAction(stringIDToTypeID('setDocumentDuration'),d,DialogModes.NO);return 'ok';");
816
- jsxTool("render_video_to_file", "Renderiza el timeline a un archivo de video (File > Export > Render Video, config básica).", { path: S.string() },
816
+ jsxTool("render_video_to_file", "Renders the timeline to a video file (File > Export > Render Video, basic config).", { path: S.string() },
817
817
  "var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));executeAction(stringIDToTypeID('exportMovie'),d,DialogModes.NO);return {path:A.path};", 600000);
818
818
 
819
819
  /* ====================== CAMERA RAW ====================== */
820
- jsxTool("apply_camera_raw_filter", "Aplica el filtro Camera Raw a la capa (abre con última config o defaults; best-effort, algunas builds requieren UI).", { name: S.string().optional() },
820
+ jsxTool("apply_camera_raw_filter", "Applies the Camera Raw filter to the layer (opens with the last config or defaults; best-effort, some builds require UI).", { name: S.string().optional() },
821
821
  "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('cameraRawFilter'),new ActionDescriptor(),DialogModes.NO);return 'ok';", 180000);
822
- jsxTool("open_raw_with_settings", "Abre un archivo RAW aplicando ajustes básicos (temperature/tint/exposure/contrast).", { path: S.string(), temperature: S.number().optional(), tint: S.number().optional(), exposure: S.number().optional(), contrast: S.number().optional() },
823
- "var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));if(A.temperature!=null)d.putInteger(stringIDToTypeID('temperature'),A.temperature);if(A.tint!=null)d.putInteger(stringIDToTypeID('tint'),A.tint);if(A.exposure!=null)d.putDouble(stringIDToTypeID('exposure'),A.exposure);if(A.contrast!=null)d.putInteger(stringIDToTypeID('contrast'),A.contrast);executeAction(stringIDToTypeID('cameraRawOpenDocument'),d,DialogModes.NO);return 'abierto: '+app.activeDocument.name;", 120000);
822
+ jsxTool("open_raw_with_settings", "Opens a RAW file applying basic adjustments (temperature/tint/exposure/contrast).", { path: S.string(), temperature: S.number().optional(), tint: S.number().optional(), exposure: S.number().optional(), contrast: S.number().optional() },
823
+ "var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));if(A.temperature!=null)d.putInteger(stringIDToTypeID('temperature'),A.temperature);if(A.tint!=null)d.putInteger(stringIDToTypeID('tint'),A.tint);if(A.exposure!=null)d.putDouble(stringIDToTypeID('exposure'),A.exposure);if(A.contrast!=null)d.putInteger(stringIDToTypeID('contrast'),A.contrast);executeAction(stringIDToTypeID('cameraRawOpenDocument'),d,DialogModes.NO);return 'opened: '+app.activeDocument.name;", 120000);
824
824
 
825
825
  /* ====================== PRESETS: PATRONES / MUESTRAS / DEGRADADOS / PINCELES ====================== */
826
- jsxTool("define_pattern_from_selection", "Define un patrón desde la selección o el documento completo.", { name: S.string() },
827
- "var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.name);executeAction(stringIDToTypeID('define'),d,DialogModes.NO);return 'patrón: '+A.name;");
828
- jsxTool("define_custom_shape_from_path", "Define una forma personalizada desde una ruta.", { pathName: S.string(), name: S.string() },
829
- "_doc().pathItems.getByName(A.pathName).select();var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.name);var r=new ActionReference();r.putClass(stringIDToTypeID('customShape'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'forma: '+A.name;");
830
- jsxTool("add_swatch", "Agrega el color frontal actual (o uno dado) a Muestras.", { name: S.string().optional(), color: S.string().optional() },
831
- "if(A.color)app.foregroundColor=_hex(A.color);var d=new ActionDescriptor();if(A.name)d.putString(charIDToTypeID('Nm '),A.name);var r=new ActionReference();r.putClass(stringIDToTypeID('colorSwatch'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'muestra agregada';");
832
- jsxTool("save_gradient_preset", "Guarda el degradado actual (foreground->background por defecto) como preset con nombre.", { name: S.string(), from: S.string().optional(), to: S.string().optional() },
833
- "app.foregroundColor=_hex(A.from||'#000000');app.backgroundColor=_hex(A.to||'#FFFFFF');var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.name);var r=new ActionReference();r.putClass(stringIDToTypeID('gradientClass'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'degradado: '+A.name;");
834
- jsxTool("set_brush_preset", "[NO VERIFICADO — usa la misma referencia AM al pincel de trabajo que resultó rota en set_brush_size; no probado en vivo, probablemente falle igual] Selecciona un preset de pincel por nombre para las herramientas de pintura. Seleccionalo manualmente en el panel Pinceles si esto falla.", { name: S.string() },
835
- "var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.name);var r=new ActionReference();r.putProperty(charIDToTypeID('Brsh'),charIDToTypeID('Wrkg'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'pincel: '+A.name;");
836
- jsxTool("set_brush_size", "[NO FUNCIONAL — confirmado roto en pruebas en vivo, la referencia AM al pincel de trabajo está mal construida y no encontré la estructura correcta; probé además seleccionar la herramienta Pincel primero, sin éxito] Tamaño de pincel activo (diámetro px). Cambialo manualmente en Photoshop antes de pintar.", { size: S.number() },
826
+ jsxTool("define_pattern_from_selection", "Defines a pattern from the selection or the whole document.", { name: S.string() },
827
+ "var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.name);executeAction(stringIDToTypeID('define'),d,DialogModes.NO);return 'pattern: '+A.name;");
828
+ jsxTool("define_custom_shape_from_path", "Defines a custom shape from a path.", { pathName: S.string(), name: S.string() },
829
+ "_doc().pathItems.getByName(A.pathName).select();var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.name);var r=new ActionReference();r.putClass(stringIDToTypeID('customShape'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'shape: '+A.name;");
830
+ jsxTool("add_swatch", "Adds the current (or given) foreground color to Swatches.", { name: S.string().optional(), color: S.string().optional() },
831
+ "if(A.color)app.foregroundColor=_hex(A.color);var d=new ActionDescriptor();if(A.name)d.putString(charIDToTypeID('Nm '),A.name);var r=new ActionReference();r.putClass(stringIDToTypeID('colorSwatch'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'swatch added';");
832
+ jsxTool("save_gradient_preset", "Saves the current gradient (foreground->background by default) as a named preset.", { name: S.string(), from: S.string().optional(), to: S.string().optional() },
833
+ "app.foregroundColor=_hex(A.from||'#000000');app.backgroundColor=_hex(A.to||'#FFFFFF');var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.name);var r=new ActionReference();r.putClass(stringIDToTypeID('gradientClass'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'gradient: '+A.name;");
834
+ jsxTool("set_brush_preset", "[UNVERIFIED — uses the same AM reference to the working brush that turned out broken in set_brush_size; not tested live, will likely fail the same way] Selects a brush preset by name for the painting tools. Select it manually in the Brushes panel if this fails.", { name: S.string() },
835
+ "var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.name);var r=new ActionReference();r.putProperty(charIDToTypeID('Brsh'),charIDToTypeID('Wrkg'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'brush: '+A.name;");
836
+ jsxTool("set_brush_size", "[NOT FUNCTIONAL — confirmed broken in live testing, the AM reference to the working brush is built incorrectly and I couldn't find the right structure; I also tried selecting the Brush tool first, without success] Active brush size (diameter px). Change it manually in Photoshop before painting.", { size: S.number() },
837
837
  "var d=new ActionDescriptor();var r=new ActionReference();r.putProperty(charIDToTypeID('Brsh'),charIDToTypeID('Wrkg'));d.putReference(charIDToTypeID('null'),r);var b=new ActionDescriptor();b.putUnitDouble(charIDToTypeID('Dmtr'),charIDToTypeID('#Pxl'),A.size);d.putObject(charIDToTypeID('T '),charIDToTypeID('Brsh'),b);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok';");
838
838
 
839
839
  /* ====================== PINTURA / TRAZO CON HERRAMIENTA ====================== */
840
- jsxTool("paint_stroke", "Traza una pincelada entre puntos [[x,y],...] con la herramienta pincel y color dado. Usa el tamaño de pincel ACTUAL de Photoshop (el parámetro 'size' no se pudo aplicar de forma confiable vía Action Manager — cambiá el tamaño manualmente en Photoshop antes de llamar esto si necesitás uno específico).", { points: S.array(S.array(S.number())), color: S.string().optional(), size: S.number().optional() },
840
+ jsxTool("paint_stroke", "Strokes a brush stroke between points [[x,y],...] with the given brush tool and color. Uses Photoshop's CURRENT brush size (the 'size' parameter couldn't be applied reliably via Action Manager — change the size manually in Photoshop before calling this if you need a specific one).", { points: S.array(S.array(S.number())), color: S.string().optional(), size: S.number().optional() },
841
841
  "if(A.color)app.foregroundColor=_hex(A.color);var pts=A.points;var d=new ActionDescriptor();var r=new ActionReference();r.putEnumerated(charIDToTypeID('Lyr '),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);var list=new ActionList();for(var i=0;i<pts.length;i++){var pd=new ActionDescriptor();pd.putUnitDouble(charIDToTypeID('Hrzn'),charIDToTypeID('#Pxl'),pts[i][0]);pd.putUnitDouble(charIDToTypeID('Vrtc'),charIDToTypeID('#Pxl'),pts[i][1]);list.putObject(charIDToTypeID('Pnt '),pd);}d.putList(stringIDToTypeID('strokePoints'),list);executeAction(stringIDToTypeID('paintStroke'),d,DialogModes.NO);return 'ok';");
842
- jsxTool("eraser_stroke", "Traza con la goma entre puntos [[x,y],...]. Usa el tamaño de pincel ACTUAL de Photoshop (ver nota en paint_stroke).", { points: S.array(S.array(S.number())), size: S.number().optional() },
842
+ jsxTool("eraser_stroke", "Strokes with the eraser between points [[x,y],...]. Uses Photoshop's CURRENT brush size (see note in paint_stroke).", { points: S.array(S.array(S.number())), size: S.number().optional() },
843
843
  "var pts=A.points;var d=new ActionDescriptor();var list=new ActionList();for(var i=0;i<pts.length;i++){var pd=new ActionDescriptor();pd.putUnitDouble(charIDToTypeID('Hrzn'),charIDToTypeID('#Pxl'),pts[i][0]);pd.putUnitDouble(charIDToTypeID('Vrtc'),charIDToTypeID('#Pxl'),pts[i][1]);list.putObject(charIDToTypeID('Pnt '),pd);}d.putList(stringIDToTypeID('strokePoints'),list);executeAction(stringIDToTypeID('eraseStroke'),d,DialogModes.NO);return 'ok';");
844
- jsxTool("clone_stamp_stroke", "Clona desde un punto origen a lo largo de puntos destino (Clone Stamp, best-effort). Usa el tamaño de pincel ACTUAL de Photoshop (ver nota en paint_stroke).", { source: S.array(S.number()), points: S.array(S.array(S.number())), size: S.number().optional() },
844
+ jsxTool("clone_stamp_stroke", "Clones from a source point along destination points (Clone Stamp, best-effort). Uses Photoshop's CURRENT brush size (see note in paint_stroke).", { source: S.array(S.number()), points: S.array(S.array(S.number())), size: S.number().optional() },
845
845
  "var d=new ActionDescriptor();var src=new ActionDescriptor();src.putUnitDouble(charIDToTypeID('Hrzn'),charIDToTypeID('#Pxl'),A.source[0]);src.putUnitDouble(charIDToTypeID('Vrtc'),charIDToTypeID('#Pxl'),A.source[1]);d.putObject(stringIDToTypeID('alignedSource'),charIDToTypeID('Pnt '),src);var list=new ActionList();for(var i=0;i<A.points.length;i++){var pd=new ActionDescriptor();pd.putUnitDouble(charIDToTypeID('Hrzn'),charIDToTypeID('#Pxl'),A.points[i][0]);pd.putUnitDouble(charIDToTypeID('Vrtc'),charIDToTypeID('#Pxl'),A.points[i][1]);list.putObject(charIDToTypeID('Pnt '),pd);}d.putList(stringIDToTypeID('strokePoints'),list);executeAction(stringIDToTypeID('cloneStampStroke'),d,DialogModes.NO);return 'ok';");
846
- jsxTool("gradient_tool_apply", "Aplica un degradado con la herramienta Degradado entre 2 puntos (from->to hex).", { x1: S.number(), y1: S.number(), x2: S.number(), y2: S.number(), fromColor: S.string().optional(), toColor: S.string().optional(), name: S.string().optional() },
846
+ jsxTool("gradient_tool_apply", "Applies a gradient with the Gradient tool between 2 points (from->to hex).", { x1: S.number(), y1: S.number(), x2: S.number(), y2: S.number(), fromColor: S.string().optional(), toColor: S.string().optional(), name: S.string().optional() },
847
847
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();d.putObject(charIDToTypeID('Grad'),charIDToTypeID('Grdn'),_grad(A.fromColor||'#000000',A.toColor||'#FFFFFF'));var from=new ActionDescriptor();from.putUnitDouble(charIDToTypeID('Hrzn'),charIDToTypeID('#Pxl'),A.x1);from.putUnitDouble(charIDToTypeID('Vrtc'),charIDToTypeID('#Pxl'),A.y1);d.putObject(charIDToTypeID('From'),charIDToTypeID('Pnt '),from);var to=new ActionDescriptor();to.putUnitDouble(charIDToTypeID('Hrzn'),charIDToTypeID('#Pxl'),A.x2);to.putUnitDouble(charIDToTypeID('Vrtc'),charIDToTypeID('#Pxl'),A.y2);d.putObject(charIDToTypeID('T '),charIDToTypeID('Pnt '),to);d.putEnumerated(charIDToTypeID('Type'),charIDToTypeID('GrdT'),charIDToTypeID('Lnr '));executeAction(charIDToTypeID('Grdn'),d,DialogModes.NO);return 'ok';");
848
848
 
849
849
  /* ====================== PREFERENCIAS ====================== */
850
- jsxTool("get_preferences", "Lee preferencias generales relevantes (unidades, interpolación, historia, etc.).", {},
850
+ jsxTool("get_preferences", "Reads relevant general preferences (units, interpolation, history, etc.).", {},
851
851
  "var p=app.preferences;return {rulerUnits:String(p.rulerUnits).replace('Units.',''),typeUnits:String(p.typeUnits).replace('TypeUnits.',''),interpolation:String(p.interpolation).replace('ResampleMethod.',''),numberOfHistoryStates:p.numberOfHistoryStates};");
852
- jsxTool("set_type_units", "Unidades de texto (PIXELS|POINTS|MM).", { units: S.string() }, "app.preferences.typeUnits=TypeUnits[A.units];return 'ok';");
853
- jsxTool("set_interpolation_method", "Método de interpolación por defecto.", { method: S.string() }, "app.preferences.interpolation=ResampleMethod[A.method];return 'ok';");
854
- jsxTool("set_history_states_count", "Cantidad de estados de historia a recordar.", { count: S.number() }, "app.preferences.numberOfHistoryStates=A.count;return 'ok';");
855
- jsxTool("set_grid_preferences", "Config de la grilla (gridSize px, subdivisions).", { gridSize: S.number().optional(), subdivisions: S.number().optional() },
852
+ jsxTool("set_type_units", "Type units (PIXELS|POINTS|MM).", { units: S.string() }, "app.preferences.typeUnits=TypeUnits[A.units];return 'ok';");
853
+ jsxTool("set_interpolation_method", "Default interpolation method.", { method: S.string() }, "app.preferences.interpolation=ResampleMethod[A.method];return 'ok';");
854
+ jsxTool("set_history_states_count", "Number of history states to remember.", { count: S.number() }, "app.preferences.numberOfHistoryStates=A.count;return 'ok';");
855
+ jsxTool("set_grid_preferences", "Grid config (gridSize px, subdivisions).", { gridSize: S.number().optional(), subdivisions: S.number().optional() },
856
856
  "if(A.gridSize)app.preferences.gridSize=A.gridSize;if(A.subdivisions)app.preferences.gridSubDivisions=A.subdivisions;return 'ok';");
857
- jsxTool("get_scratch_disks", "Lista los discos de trabajo configurados (best-effort vía AM).", {},
858
- "var r=new ActionReference();r.putProperty(stringIDToTypeID('scratchDisks'),stringIDToTypeID('scratchDisks'));try{var d=executeActionGet(r);return {info:'ok'};}catch(e){return {info:'no disponible por AM en esta build'};}");
859
- jsxTool("set_undo_shortcuts", "Activa/desactiva atajo 'Ctrl+Z' con historial extendido (preferencia de interfaz, PS 2020+).", { extendedUndo: S.boolean() },
857
+ jsxTool("get_scratch_disks", "Lists the configured scratch disks (best-effort via AM).", {},
858
+ "var r=new ActionReference();r.putProperty(stringIDToTypeID('scratchDisks'),stringIDToTypeID('scratchDisks'));try{var d=executeActionGet(r);return {info:'ok'};}catch(e){return {info:'not available via AM in this build'};}");
859
+ jsxTool("set_undo_shortcuts", "Enables/disables the 'Ctrl+Z' shortcut with extended history (interface preference, PS 2020+).", { extendedUndo: S.boolean() },
860
860
  "try{app.preferences.interfacePreferences=A.extendedUndo;}catch(e){}return 'ok (best-effort)';");
861
- jsxTool("set_color_picker_hue", "Selecciona el color frontal a partir de HSB.", { h: S.number(), s: S.number(), b: S.number() },
861
+ jsxTool("set_color_picker_hue", "Selects the foreground color from HSB.", { h: S.number(), s: S.number(), b: S.number() },
862
862
  "var c=new SolidColor();c.hsb.hue=A.h;c.hsb.saturation=A.s;c.hsb.brightness=A.b;app.foregroundColor=c;return {hex:'#'+c.rgb.hexValue.toUpperCase()};");
863
863
 
864
864
  /* ====================== BATCH SOBRE CARPETA / IMAGE PROCESSOR ====================== */
865
- jsxTool("batch_run_action_on_folder", "Corre una acción sobre todos los archivos de una carpeta (batchPlay File>Automate>Batch).", { set: S.string(), action: S.string(), sourceFolder: S.string(), destFolder: S.string().optional(), fileType: S.string().optional() },
866
- "var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.action);d.putString(stringIDToTypeID('actionSetName'),A.set);d.putPath(stringIDToTypeID('sourceFolder'),new Folder(A.sourceFolder));if(A.destFolder){d.putEnumerated(stringIDToTypeID('destination'),stringIDToTypeID('destination'),stringIDToTypeID('folder'));d.putPath(stringIDToTypeID('destinationFolder'),new Folder(A.destFolder));}else{d.putEnumerated(stringIDToTypeID('destination'),stringIDToTypeID('destination'),stringIDToTypeID('none'));}executeAction(stringIDToTypeID('batch'),d,DialogModes.NO);return 'batch ejecutado';", 600000);
867
- jsxTool("image_processor_folder", "Procesa todas las imágenes de una carpeta: redimensiona y exporta a JPG/PSD/TIFF.", { sourceFolder: S.string(), destFolder: S.string(), maxWidth: S.number().optional(), maxHeight: S.number().optional(), format: S.string().optional(), quality: S.number().optional() },
865
+ jsxTool("batch_run_action_on_folder", "Runs an action over all files in a folder (batchPlay File>Automate>Batch).", { set: S.string(), action: S.string(), sourceFolder: S.string(), destFolder: S.string().optional(), fileType: S.string().optional() },
866
+ "var d=new ActionDescriptor();d.putString(charIDToTypeID('Nm '),A.action);d.putString(stringIDToTypeID('actionSetName'),A.set);d.putPath(stringIDToTypeID('sourceFolder'),new Folder(A.sourceFolder));if(A.destFolder){d.putEnumerated(stringIDToTypeID('destination'),stringIDToTypeID('destination'),stringIDToTypeID('folder'));d.putPath(stringIDToTypeID('destinationFolder'),new Folder(A.destFolder));}else{d.putEnumerated(stringIDToTypeID('destination'),stringIDToTypeID('destination'),stringIDToTypeID('none'));}executeAction(stringIDToTypeID('batch'),d,DialogModes.NO);return 'batch executed';", 600000);
867
+ jsxTool("image_processor_folder", "Processes all images in a folder: resizes and exports to JPG/PSD/TIFF.", { sourceFolder: S.string(), destFolder: S.string(), maxWidth: S.number().optional(), maxHeight: S.number().optional(), format: S.string().optional(), quality: S.number().optional() },
868
868
  "var src=new Folder(A.sourceFolder);var dst=new Folder(A.destFolder);if(!dst.exists)dst.create();var files=src.getFiles(function(f){return f instanceof File && /\\.(jpg|jpeg|png|tif|tiff|psd|bmp)$/i.test(f.name);});var out=[];for(var i=0;i<files.length;i++){var doc=app.open(files[i]);if(A.maxWidth||A.maxHeight){var w=doc.width.as('px'),h=doc.height.as('px');var scale=Math.min((A.maxWidth||w)/w,(A.maxHeight||h)/h,1);if(scale<1)doc.resizeImage(UnitValue(w*scale,'px'),UnitValue(h*scale,'px'));}var base=files[i].name.replace(/\\.[^.]+$/,'');var fmt=(A.format||'jpg').toLowerCase();var outFile;if(fmt==='png'){outFile=new File(A.destFolder+'/'+base+'.png');var o=new PNGSaveOptions();doc.saveAs(outFile,o,true,Extension.LOWERCASE);}else if(fmt==='tif'||fmt==='tiff'){outFile=new File(A.destFolder+'/'+base+'.tif');var o2=new TiffSaveOptions();doc.saveAs(outFile,o2,true,Extension.LOWERCASE);}else{outFile=new File(A.destFolder+'/'+base+'.jpg');var o3=new JPEGSaveOptions();o3.quality=A.quality||10;doc.saveAs(outFile,o3,true,Extension.LOWERCASE);}doc.close(SaveOptions.DONOTSAVECHANGES);out.push(outFile.fsName);}return {processed:out.length,files:out};", 600000);
869
- jsxTool("run_jsx_on_folder", "Aplica un snippet ExtendScript a cada archivo de una carpeta (abre, corre code con 'doc' disponible, guarda y cierra).", { sourceFolder: S.string(), code: S.string(), save: S.boolean().optional(), extensions: S.array(S.string()).optional() },
869
+ jsxTool("run_jsx_on_folder", "Applies an ExtendScript snippet to every file in a folder (opens, runs code with 'doc' available, saves and closes).", { sourceFolder: S.string(), code: S.string(), save: S.boolean().optional(), extensions: S.array(S.string()).optional() },
870
870
  "var src=new Folder(A.sourceFolder);var exts=(A.extensions&&A.extensions.length)?A.extensions:['jpg','jpeg','png','tif','tiff','psd'];var re=new RegExp('\\\\.('+exts.join('|')+')$','i');var files=src.getFiles(function(f){return f instanceof File && re.test(f.name);});var out=[];for(var i=0;i<files.length;i++){var doc=app.open(files[i]);try{var fn=new Function('doc',A.code);fn(doc);}catch(e){out.push({file:files[i].name,error:String(e)});doc.close(SaveOptions.DONOTSAVECHANGES);continue;}if(A.save)doc.save();doc.close(A.save?SaveOptions.SAVECHANGES:SaveOptions.DONOTSAVECHANGES);out.push({file:files[i].name,ok:true});}return out;", 600000);
871
871
 
872
872
  /* ====================== NOTAS / ANOTACIONES ====================== */
873
- jsxTool("add_note", "Agrega una nota de texto anclada en (x,y).", { text: S.string(), x: S.number().optional(), y: S.number().optional(), author: S.string().optional() },
874
- "var n=_doc().notes.add([UnitValue(A.x||0,'px'),UnitValue(A.y||0,'px')],A.author||'Claude',A.text);return 'nota agregada';");
875
- jsxTool("list_notes", "Lista las notas del documento.", {}, "var a=[];var n=_doc().notes;for(var i=0;i<n.length;i++)a.push({author:n[i].author,note:n[i].note});return a;");
876
- jsxTool("delete_all_notes", "Elimina todas las notas del documento.", {}, "var n=_doc().notes;while(n.length)n[0].remove();return 'ok';");
873
+ jsxTool("add_note", "Adds a text note anchored at (x,y).", { text: S.string(), x: S.number().optional(), y: S.number().optional(), author: S.string().optional() },
874
+ "var n=_doc().notes.add([UnitValue(A.x||0,'px'),UnitValue(A.y||0,'px')],A.author||'Claude',A.text);return 'note added';");
875
+ jsxTool("list_notes", "Lists the document's notes.", {}, "var a=[];var n=_doc().notes;for(var i=0;i<n.length;i++)a.push({author:n[i].author,note:n[i].note});return a;");
876
+ jsxTool("delete_all_notes", "Deletes all of the document's notes.", {}, "var n=_doc().notes;while(n.length)n[0].remove();return 'ok';");
877
877
 
878
878
  /* ====================== FRAME TOOL ====================== */
879
- jsxTool("create_frame_from_shape", "Convierte una capa de forma (rect/ellipse) en Frame (marco de reemplazo de imagen).", { name: S.string() },
879
+ jsxTool("create_frame_from_shape", "Converts a shape layer (rect/ellipse) into a Frame (image-replacement frame).", { name: S.string() },
880
880
  "_sel(_need(A.name));executeAction(stringIDToTypeID('newFrame'),new ActionDescriptor(),DialogModes.NO);return 'ok';");
881
- jsxTool("place_into_frame", "Coloca un archivo dentro de un Frame existente.", { frameName: S.string(), path: S.string() },
881
+ jsxTool("place_into_frame", "Places a file inside an existing Frame.", { frameName: S.string(), path: S.string() },
882
882
  "_sel(_need(A.frameName));var d=new ActionDescriptor();d.putPath(charIDToTypeID('null'),new File(A.path));executeAction(stringIDToTypeID('placeInsideFrame'),d,DialogModes.NO);return 'ok';");
883
883
 
884
884
  /* ====================== TEXTO EN TRAZADO / MÁS FORMAS ====================== */
885
- jsxTool("add_text_on_path", "Crea texto siguiendo una ruta existente.", { pathName: S.string(), text: S.string(), size: S.number().optional(), font: S.string().optional() },
886
- "var p=_doc().pathItems.getByName(A.pathName);p.select();var L=_doc().artLayers.add();L.kind=LayerKind.TEXT;var t=L.textItem;t.contents=A.text;if(A.size)t.size=A.size;if(A.font)t.font=A.font;var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);var r2=new ActionReference();r2.putName(charIDToTypeID('Path'),A.pathName);d.putReference(stringIDToTypeID('workPath'),r2);executeAction(stringIDToTypeID('attachTextToPath'),d,DialogModes.NO);return 'texto en ruta: '+L.name;");
887
- jsxTool("add_shape_polygon", "Crea una capa de forma polígono (sides, radius px, centro x,y).", { x: S.number(), y: S.number(), radius: S.number(), sides: S.number().optional(), color: S.string().optional() },
888
- "var n=A.sides||5;var pts=[];for(var i=0;i<n;i++){var ang=(Math.PI*2*i/n)-Math.PI/2;pts.push([A.x+A.radius*Math.cos(ang),A.y+A.radius*Math.sin(ang)]);}var pp=[];for(var j=0;j<pts.length;j++){var info=new PathPointInfo();info.kind=PointKind.CORNERPOINT;info.anchor=pts[j];info.leftDirection=info.anchor;info.rightDirection=info.anchor;pp.push(info);}var sp=new SubPathInfo();sp.closed=true;sp.operation=ShapeOperation.SHAPEADD;sp.entireSubPath=pp;var path=_doc().pathItems.add('__polyTmp',[sp]);path.select();app.foregroundColor=_hex(A.color||'#808080');var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'polígono';");
889
- jsxTool("add_shape_line", "Dibuja una línea entre dos puntos (RASTERIZADA sobre una capa nueva, no es una capa de forma vectorial — la 'capa de forma línea' vía Action Manager no tiene un ID confiable/documentado y falló en pruebas en vivo; este método usa PathItem.strokePath, confirmado funcionando). Usa el grosor del pincel ACTUAL de Photoshop (el parámetro 'weight' no se pudo aplicar de forma confiable vía Action Manager — cambialo manualmente si necesitás uno específico).", { x1: S.number(), y1: S.number(), x2: S.number(), y2: S.number(), weight: S.number().optional(), color: S.string().optional(), name: S.string().optional() },
890
- "app.foregroundColor=_hex(A.color||'#000000');var L=_doc().artLayers.add();if(A.name)L.name=A.name;_sel(L);var p1=new PathPointInfo();p1.kind=PointKind.CORNERPOINT;p1.anchor=[A.x1,A.y1];p1.leftDirection=p1.anchor;p1.rightDirection=p1.anchor;var p2=new PathPointInfo();p2.kind=PointKind.CORNERPOINT;p2.anchor=[A.x2,A.y2];p2.leftDirection=p2.anchor;p2.rightDirection=p2.anchor;var sp=new SubPathInfo();sp.closed=false;sp.operation=ShapeOperation.SHAPEADD;sp.entireSubPath=[p1,p2];var path=_doc().pathItems.add('__lineTmp__',[sp]);path.strokePath(ToolType.PENCIL);path.remove();return 'línea trazada (rasterizada) en: '+L.name;");
891
- jsxTool("boolean_path_operation", "Combina dos rutas con una operación booleana (union|subtract|intersect) → nueva ruta. 'exclude' (diferencia simétrica) no está soportado en un solo paso: hacé union y subtract(intersect) por separado.", { pathA: S.string(), pathB: S.string(), operation: S.string(), resultName: S.string().optional() },
892
- "var a=_doc().pathItems.getByName(A.pathA);var b=_doc().pathItems.getByName(A.pathB);var opMap={union:SelectionType.EXTEND,subtract:SelectionType.DIMINISH,intersect:SelectionType.INTERSECT};var sel=opMap[A.operation];if(!sel)throw 'operation inválida (union|subtract|intersect): '+A.operation;a.makeSelection(0,false,SelectionType.REPLACE);b.makeSelection(0,false,sel);_doc().selection.makeWorkPath(2);var wp=_doc().pathItems.getByName('Work Path');if(A.resultName)wp.name=A.resultName;_doc().selection.deselect();return 'ok: '+wp.name;");
885
+ jsxTool("add_text_on_path", "Creates text following an existing path.", { pathName: S.string(), text: S.string(), size: S.number().optional(), font: S.string().optional() },
886
+ "var p=_doc().pathItems.getByName(A.pathName);p.select();var L=_doc().artLayers.add();L.kind=LayerKind.TEXT;var t=L.textItem;t.contents=A.text;if(A.size)t.size=A.size;if(A.font)t.font=A.font;var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);var r2=new ActionReference();r2.putName(charIDToTypeID('Path'),A.pathName);d.putReference(stringIDToTypeID('workPath'),r2);executeAction(stringIDToTypeID('attachTextToPath'),d,DialogModes.NO);return 'text on path: '+L.name;");
887
+ jsxTool("add_shape_polygon", "Creates a polygon shape layer (sides, radius px, center x,y).", { x: S.number(), y: S.number(), radius: S.number(), sides: S.number().optional(), color: S.string().optional() },
888
+ "var n=A.sides||5;var pts=[];for(var i=0;i<n;i++){var ang=(Math.PI*2*i/n)-Math.PI/2;pts.push([A.x+A.radius*Math.cos(ang),A.y+A.radius*Math.sin(ang)]);}var pp=[];for(var j=0;j<pts.length;j++){var info=new PathPointInfo();info.kind=PointKind.CORNERPOINT;info.anchor=pts[j];info.leftDirection=info.anchor;info.rightDirection=info.anchor;pp.push(info);}var sp=new SubPathInfo();sp.closed=true;sp.operation=ShapeOperation.SHAPEADD;sp.entireSubPath=pp;var path=_doc().pathItems.add('__polyTmp',[sp]);path.select();app.foregroundColor=_hex(A.color||'#808080');var d=new ActionDescriptor();var r=new ActionReference();r.putClass(stringIDToTypeID('contentLayer'));d.putReference(charIDToTypeID('null'),r);var ct=new ActionDescriptor();var fill=new ActionDescriptor();fill.putObject(charIDToTypeID('Clr '),charIDToTypeID('RGBC'),_cD(A.color||'#808080'));ct.putObject(charIDToTypeID('Type'),stringIDToTypeID('solidColorLayer'),fill);d.putObject(charIDToTypeID('Usng'),stringIDToTypeID('contentLayer'),ct);executeAction(charIDToTypeID('Mk '),d,DialogModes.NO);return 'polygon';");
889
+ jsxTool("add_shape_line", "Draws a line between two points (RASTERIZED onto a new layer, not a real vector shape layer — the 'line shape layer' via Action Manager doesn't have a reliable/documented ID and failed in live testing; this method uses PathItem.strokePath, confirmed working). Uses Photoshop's CURRENT brush weight (the 'weight' parameter couldn't be applied reliably via Action Manager — change it manually if you need a specific one).", { x1: S.number(), y1: S.number(), x2: S.number(), y2: S.number(), weight: S.number().optional(), color: S.string().optional(), name: S.string().optional() },
890
+ "app.foregroundColor=_hex(A.color||'#000000');var L=_doc().artLayers.add();if(A.name)L.name=A.name;_sel(L);var p1=new PathPointInfo();p1.kind=PointKind.CORNERPOINT;p1.anchor=[A.x1,A.y1];p1.leftDirection=p1.anchor;p1.rightDirection=p1.anchor;var p2=new PathPointInfo();p2.kind=PointKind.CORNERPOINT;p2.anchor=[A.x2,A.y2];p2.leftDirection=p2.anchor;p2.rightDirection=p2.anchor;var sp=new SubPathInfo();sp.closed=false;sp.operation=ShapeOperation.SHAPEADD;sp.entireSubPath=[p1,p2];var path=_doc().pathItems.add('__lineTmp__',[sp]);path.strokePath(ToolType.PENCIL);path.remove();return 'line stroked (rasterized) on: '+L.name;");
891
+ jsxTool("boolean_path_operation", "Combines two paths with a boolean operation (union|subtract|intersect) → new path. 'exclude' (symmetric difference) isn't supported in a single step: do union and subtract(intersect) separately.", { pathA: S.string(), pathB: S.string(), operation: S.string(), resultName: S.string().optional() },
892
+ "var a=_doc().pathItems.getByName(A.pathA);var b=_doc().pathItems.getByName(A.pathB);var opMap={union:SelectionType.EXTEND,subtract:SelectionType.DIMINISH,intersect:SelectionType.INTERSECT};var sel=opMap[A.operation];if(!sel)throw 'invalid operation (union|subtract|intersect): '+A.operation;a.makeSelection(0,false,SelectionType.REPLACE);b.makeSelection(0,false,sel);_doc().selection.makeWorkPath(2);var wp=_doc().pathItems.getByName('Work Path');if(A.resultName)wp.name=A.resultName;_doc().selection.deselect();return 'ok: '+wp.name;");
893
893
 
894
894
  /* ====================== CAPAS DE AJUSTE (avanzadas: selective color, channel mixer, curves con puntos) ====================== */
895
- jsxTool("add_adjustment_selective_color", "Capa de ajuste Color selectivo para un canal (reds|yellows|greens|cyans|blues|magentas|whites|neutrals|blacks).", { channel: S.string(), cyan: S.number().optional(), magenta: S.number().optional(), yellow: S.number().optional(), black: S.number().optional(), method: S.string().optional() },
895
+ jsxTool("add_adjustment_selective_color", "Selective Color adjustment layer for a channel (reds|yellows|greens|cyans|blues|magentas|whites|neutrals|blacks).", { channel: S.string(), cyan: S.number().optional(), magenta: S.number().optional(), yellow: S.number().optional(), black: S.number().optional(), method: S.string().optional() },
896
896
  "var chMap={reds:charIDToTypeID('Rds '),yellows:charIDToTypeID('Ylws'),greens:charIDToTypeID('Grns'),cyans:charIDToTypeID('Cyns'),blues:charIDToTypeID('Blus'),magentas:charIDToTypeID('Mgnt'),whites:charIDToTypeID('Whts'),neutrals:charIDToTypeID('Ntrl'),blacks:charIDToTypeID('Blck')};var a=new ActionDescriptor();a.putEnumerated(stringIDToTypeID('presetKind'),stringIDToTypeID('presetKindType'),stringIDToTypeID('presetKindCustom'));a.putEnumerated(charIDToTypeID('Mthd'),charIDToTypeID('Mthd'),A.method==='absolute'?charIDToTypeID('Abst'):charIDToTypeID('Rltv'));var list=new ActionList();var ch=new ActionDescriptor();ch.putEnumerated(charIDToTypeID('Clr '),charIDToTypeID('Clr '),chMap[A.channel]||charIDToTypeID('Rds '));ch.putInteger(charIDToTypeID('Cyn '),A.cyan||0);ch.putInteger(charIDToTypeID('Mgnt'),A.magenta||0);ch.putInteger(charIDToTypeID('Ylw '),A.yellow||0);ch.putInteger(charIDToTypeID('Blck'),A.black||0);list.putObject(charIDToTypeID('SlcC'),ch);a.putList(charIDToTypeID('Clrs'),list);_mkAdj(stringIDToTypeID('selectiveColor'),a);return 'ok';");
897
- jsxTool("add_adjustment_channel_mixer", "Capa de ajuste Mezclador de canales (para canal 'red'|'green'|'blue', aportes de cada canal fuente %).", { outputChannel: S.string(), red: S.number().optional(), green: S.number().optional(), blue: S.number().optional(), constant: S.number().optional(), monochrome: S.boolean().optional() },
897
+ jsxTool("add_adjustment_channel_mixer", "Channel Mixer adjustment layer (for channel 'red'|'green'|'blue', contribution from each source channel %).", { outputChannel: S.string(), red: S.number().optional(), green: S.number().optional(), blue: S.number().optional(), constant: S.number().optional(), monochrome: S.boolean().optional() },
898
898
  "var a=new ActionDescriptor();a.putBoolean(stringIDToTypeID('monochrome'),!!A.monochrome);var ch=new ActionDescriptor();ch.putInteger(charIDToTypeID('Rd '),A.red!=null?A.red:100);ch.putInteger(charIDToTypeID('Grn '),A.green||0);ch.putInteger(charIDToTypeID('Bl '),A.blue||0);ch.putInteger(charIDToTypeID('Cnst'),A.constant||0);var outMap={red:charIDToTypeID('Rd '),green:charIDToTypeID('Grn '),blue:charIDToTypeID('Bl ')};a.putObject(outMap[A.outputChannel]||charIDToTypeID('Rd '),charIDToTypeID('ChMx'),ch);_mkAdj(stringIDToTypeID('channelMixer'),a);return 'ok';");
899
- jsxTool("add_adjustment_curves_points", "Capa de ajuste Curvas con valores por defecto (NO soporta puntos custom vía Action Manager — falló en pruebas en vivo con estructura de puntos inventada; no hay estructura AM confiable documentada para inyectar puntos directamente). Creá la capa y ajustala manualmente, o usá la tool destructiva 'adjust_curves' (DOM, confirmada funcionando) si necesitás puntos específicos ya.", { points: S.array(S.array(S.number())).optional() },
900
- "_mkAdj(stringIDToTypeID('curves'),new ActionDescriptor());return 'ok (capa creada con valores por defecto; el parámetro points se ignora, ver descripción)';");
901
- jsxTool("add_adjustment_levels_params", "Capa de ajuste Niveles con valores por defecto (NO soporta parámetros custom vía Action Manager — falló en pruebas en vivo con la estructura de rango inventada). Creá la capa y ajustala manualmente, o usá la tool destructiva 'adjust_levels' (DOM, confirmada funcionando) si necesitás valores específicos ya.", { inBlack: S.number().optional(), inWhite: S.number().optional(), gamma: S.number().optional(), outBlack: S.number().optional(), outWhite: S.number().optional() },
902
- "_mkAdj(stringIDToTypeID('levels'),new ActionDescriptor());return 'ok (capa creada con valores por defecto; los parámetros se ignoran, ver descripción)';");
903
- jsxTool("add_adjustment_color_balance", "Capa de ajuste Balance de color con valores por defecto (NO soporta parámetros custom vía Action Manager — falló en pruebas en vivo, 'parámetros no válidos'). Creá la capa y ajustala manualmente, o usá la tool destructiva 'adjust_color_balance' (DOM, confirmada funcionando) si necesitás valores específicos ya.", { shadows: S.array(S.number()).optional(), midtones: S.array(S.number()).optional(), highlights: S.array(S.number()).optional(), preserveLuminosity: S.boolean().optional() },
904
- "_mkAdj(stringIDToTypeID('colorBalance'),new ActionDescriptor());return 'ok (capa creada con valores por defecto; los parámetros se ignoran, ver descripción)';");
905
- jsxTool("add_adjustment_invert", "Capa de ajuste Invertir.", {}, "_mkAdj(stringIDToTypeID('invert'),new ActionDescriptor());return 'ok';");
906
- jsxTool("add_adjustment_posterize", "Capa de ajuste Posterizar.", { levels: S.number().optional() }, "var a=new ActionDescriptor();a.putInteger(charIDToTypeID('Lvls'),A.levels||4);_mkAdj(stringIDToTypeID('posterization'),a);return 'ok';");
907
- jsxTool("add_adjustment_threshold", "Capa de ajuste Umbral.", { level: S.number().optional() }, "var a=new ActionDescriptor();a.putInteger(charIDToTypeID('Lvl '),A.level||128);_mkAdj(stringIDToTypeID('thresholdClassEvent'),a);return 'ok';");
899
+ jsxTool("add_adjustment_curves_points", "Curves adjustment layer with default values (does NOT support custom points via Action Manager — failed in live testing with a made-up point structure; there's no reliable documented AM structure to inject points directly). Create the layer and adjust it manually, or use the destructive 'adjust_curves' tool (DOM, confirmed working) if you need specific points right away.", { points: S.array(S.array(S.number())).optional() },
900
+ "_mkAdj(stringIDToTypeID('curves'),new ActionDescriptor());return 'ok (layer created with default values; the points parameter is ignored, see description)';");
901
+ jsxTool("add_adjustment_levels_params", "Levels adjustment layer with default values (does NOT support custom parameters via Action Manager — failed in live testing with a made-up range structure). Create the layer and adjust it manually, or use the destructive 'adjust_levels' tool (DOM, confirmed working) if you need specific values right away.", { inBlack: S.number().optional(), inWhite: S.number().optional(), gamma: S.number().optional(), outBlack: S.number().optional(), outWhite: S.number().optional() },
902
+ "_mkAdj(stringIDToTypeID('levels'),new ActionDescriptor());return 'ok (layer created with default values; the parameters are ignored, see description)';");
903
+ jsxTool("add_adjustment_color_balance", "Color Balance adjustment layer with default values (does NOT support custom parameters via Action Manager — failed in live testing, 'invalid parameters'). Create the layer and adjust it manually, or use the destructive 'adjust_color_balance' tool (DOM, confirmed working) if you need specific values right away.", { shadows: S.array(S.number()).optional(), midtones: S.array(S.number()).optional(), highlights: S.array(S.number()).optional(), preserveLuminosity: S.boolean().optional() },
904
+ "_mkAdj(stringIDToTypeID('colorBalance'),new ActionDescriptor());return 'ok (layer created with default values; the parameters are ignored, see description)';");
905
+ jsxTool("add_adjustment_invert", "Invert adjustment layer.", {}, "_mkAdj(stringIDToTypeID('invert'),new ActionDescriptor());return 'ok';");
906
+ jsxTool("add_adjustment_posterize", "Posterize adjustment layer.", { levels: S.number().optional() }, "var a=new ActionDescriptor();a.putInteger(charIDToTypeID('Lvls'),A.levels||4);_mkAdj(stringIDToTypeID('posterization'),a);return 'ok';");
907
+ jsxTool("add_adjustment_threshold", "Threshold adjustment layer.", { level: S.number().optional() }, "var a=new ActionDescriptor();a.putInteger(charIDToTypeID('Lvl '),A.level||128);_mkAdj(stringIDToTypeID('thresholdClassEvent'),a);return 'ok';");
908
908
 
909
909
  /* ====================== EXPORT MODERNO ====================== */
910
- jsxTool("quick_export_layer", "Exporta una capa/grupo aislada con Quick Export (recorta y respeta efectos) a PNG/JPG.", { name: S.string(), path: S.string(), format: S.string().optional() },
911
- "var work=_doc().duplicate();app.activeDocument=work;var L=_find(A.name);if(!L){work.close(SaveOptions.DONOTSAVECHANGES);throw 'no encontrada: '+A.name;}function hideAll(ls){for(var i=0;i<ls.length;i++){try{ls[i].visible=false;}catch(e){}if(ls[i].typename==='LayerSet')hideAll(ls[i].layers);}}function show(n){try{n.visible=true;}catch(e){}if(n.typename==='LayerSet')for(var i=0;i<n.layers.length;i++)show(n.layers[i]);}hideAll(work.layers);show(L);var p=L.parent;while(p&&p.typename==='LayerSet'){try{p.visible=true;}catch(e){}p=p.parent;}try{work.trim(TrimType.TRANSPARENT,true,true,true,true);}catch(e){}var fmt=(A.format||'png').toLowerCase();var f=new File(A.path);if(fmt==='jpg'||fmt==='jpeg'){var o=new JPEGSaveOptions();o.quality=10;work.saveAs(f,o,true,Extension.LOWERCASE);}else{var o2=new PNGSaveOptions();work.saveAs(f,o2,true,Extension.LOWERCASE);}work.close(SaveOptions.DONOTSAVECHANGES);return {path:f.fsName};", 180000);
912
- jsxTool("export_layer_multi_size", "Exporta una capa a varios tamaños (escalas relativas, ej. [1,2,3] para @1x/@2x/@3x).", { name: S.string(), folder: S.string(), scales: S.array(S.number()), baseName: S.string().optional() },
910
+ jsxTool("quick_export_layer", "Exports an isolated layer/group with Quick Export (trims and respects effects) to PNG/JPG.", { name: S.string(), path: S.string(), format: S.string().optional() },
911
+ "var work=_doc().duplicate();app.activeDocument=work;var L=_find(A.name);if(!L){work.close(SaveOptions.DONOTSAVECHANGES);throw 'not found: '+A.name;}function hideAll(ls){for(var i=0;i<ls.length;i++){try{ls[i].visible=false;}catch(e){}if(ls[i].typename==='LayerSet')hideAll(ls[i].layers);}}function show(n){try{n.visible=true;}catch(e){}if(n.typename==='LayerSet')for(var i=0;i<n.layers.length;i++)show(n.layers[i]);}hideAll(work.layers);show(L);var p=L.parent;while(p&&p.typename==='LayerSet'){try{p.visible=true;}catch(e){}p=p.parent;}try{work.trim(TrimType.TRANSPARENT,true,true,true,true);}catch(e){}var fmt=(A.format||'png').toLowerCase();var f=new File(A.path);if(fmt==='jpg'||fmt==='jpeg'){var o=new JPEGSaveOptions();o.quality=10;work.saveAs(f,o,true,Extension.LOWERCASE);}else{var o2=new PNGSaveOptions();work.saveAs(f,o2,true,Extension.LOWERCASE);}work.close(SaveOptions.DONOTSAVECHANGES);return {path:f.fsName};", 180000);
912
+ jsxTool("export_layer_multi_size", "Exports a layer at several sizes (relative scales, e.g. [1,2,3] for @1x/@2x/@3x).", { name: S.string(), folder: S.string(), scales: S.array(S.number()), baseName: S.string().optional() },
913
913
  "var res=[];var base=A.baseName||A.name.replace(/[^a-zA-Z0-9_\\-]+/g,'_');for(var i=0;i<A.scales.length;i++){var work=_doc().duplicate();app.activeDocument=work;var L=_find(A.name);if(!L){work.close(SaveOptions.DONOTSAVECHANGES);continue;}function hideAll(ls){for(var j=0;j<ls.length;j++){try{ls[j].visible=false;}catch(e){}if(ls[j].typename==='LayerSet')hideAll(ls[j].layers);}}function show(n){try{n.visible=true;}catch(e){}if(n.typename==='LayerSet')for(var j=0;j<n.layers.length;j++)show(n.layers[j]);}hideAll(work.layers);show(L);var p=L.parent;while(p&&p.typename==='LayerSet'){try{p.visible=true;}catch(e){}p=p.parent;}try{work.trim(TrimType.TRANSPARENT,true,true,true,true);}catch(e){}var sc=A.scales[i];if(sc!==1)work.resizeImage(UnitValue(work.width.as('px')*sc,'px'),UnitValue(work.height.as('px')*sc,'px'),null,ResampleMethod.BICUBICAUTOMATIC);var f=new File(A.folder+'/'+base+'@'+sc+'x.png');var o=new PNGSaveOptions();work.saveAs(f,o,true,Extension.LOWERCASE);work.close(SaveOptions.DONOTSAVECHANGES);app.activeDocument=_doc();res.push(f.fsName);}return res;", 300000);
914
914
 
915
915
  /* ====================== AJUSTES DE COLOR (fuente cruzada) ====================== */
916
- jsxTool("match_color", "Ajusta color de la capa activa para que coincida con otro documento abierto (Match Color).", { sourceDocument: S.string(), luminance: S.number().optional(), colorIntensity: S.number().optional(), fade: S.number().optional(), neutralize: S.boolean().optional() },
916
+ jsxTool("match_color", "Adjusts the active layer's color to match another open document (Match Color).", { sourceDocument: S.string(), luminance: S.number().optional(), colorIntensity: S.number().optional(), fade: S.number().optional(), neutralize: S.boolean().optional() },
917
917
  "var d=new ActionDescriptor();var src=new ActionReference();src.putName(charIDToTypeID('Dcmn'),A.sourceDocument);d.putReference(stringIDToTypeID('sourceDocument'),src);d.putInteger(stringIDToTypeID('luminance'),A.luminance!=null?A.luminance:100);d.putInteger(stringIDToTypeID('colorIntensity'),A.colorIntensity!=null?A.colorIntensity:100);d.putInteger(stringIDToTypeID('fade'),A.fade||0);d.putBoolean(stringIDToTypeID('neutralize'),!!A.neutralize);executeAction(stringIDToTypeID('matchColor'),d,DialogModes.NO);return 'ok';");
918
- jsxTool("replace_color", "Reemplaza un rango de color por hue/sat/lightness alrededor de un pixel muestreado.", { x: S.number(), y: S.number(), fuzziness: S.number().optional(), hue: S.number().optional(), saturation: S.number().optional(), lightness: S.number().optional() },
918
+ jsxTool("replace_color", "Replaces a color range by hue/sat/lightness around a sampled pixel.", { x: S.number(), y: S.number(), fuzziness: S.number().optional(), hue: S.number().optional(), saturation: S.number().optional(), lightness: S.number().optional() },
919
919
  "var d=_doc();var s=d.colorSamplers.add([UnitValue(A.x,'px'),UnitValue(A.y,'px')]);var col=s.color;s.remove();var desc=new ActionDescriptor();desc.putInteger(stringIDToTypeID('fuzziness'),A.fuzziness||40);var c=_cD('#'+col.rgb.hexValue);desc.putObject(charIDToTypeID('Mnm '),charIDToTypeID('RGBC'),c);desc.putObject(charIDToTypeID('Mxm '),charIDToTypeID('RGBC'),c);desc.putInteger(charIDToTypeID('H '),A.hue||0);desc.putInteger(charIDToTypeID('Strt'),A.saturation||0);desc.putInteger(charIDToTypeID('Lght'),A.lightness||0);executeAction(stringIDToTypeID('replaceColor'),desc,DialogModes.NO);return 'ok';");
920
920
 
921
921
  /* ====================== TRANSFORM LIBRE (esquinas: distorsión/perspectiva) ====================== */
922
- jsxTool("transform_layer_corners", "Transformación libre por las 4 esquinas [[x,y]x4] en orden TL,TR,BR,BL (distorsión/perspectiva).", { name: S.string(), corners: S.array(S.array(S.number())) },
922
+ jsxTool("transform_layer_corners", "Free transform by the 4 corners [[x,y]x4] in order TL,TR,BR,BL (distortion/perspective).", { name: S.string(), corners: S.array(S.array(S.number())) },
923
923
  "_sel(_need(A.name));var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);d.putEnumerated(charIDToTypeID('FTcs'),charIDToTypeID('QCSt'),charIDToTypeID('Qcsa'));var list=new ActionList();for(var i=0;i<4;i++){var p=new ActionDescriptor();p.putUnitDouble(charIDToTypeID('Hrzn'),charIDToTypeID('#Pxl'),A.corners[i][0]);p.putUnitDouble(charIDToTypeID('Vrtc'),charIDToTypeID('#Pxl'),A.corners[i][1]);list.putObject(charIDToTypeID('Pnt '),p);}d.putList(stringIDToTypeID('freeTransformCorners'),list);executeAction(charIDToTypeID('Trnf'),d,DialogModes.NO);return 'ok';");
924
- jsxTool("skew_layer_precise", "Sesga una capa con ángulos horizontal/vertical precisos (grados).", { name: S.string(), horizontal: S.number().optional(), vertical: S.number().optional() },
924
+ jsxTool("skew_layer_precise", "Skews a layer with precise horizontal/vertical angles (degrees).", { name: S.string(), horizontal: S.number().optional(), vertical: S.number().optional() },
925
925
  "_sel(_need(A.name));var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);d.putUnitDouble(stringIDToTypeID('skewHorizontal'),charIDToTypeID('#Ang'),A.horizontal||0);d.putUnitDouble(stringIDToTypeID('skewVertical'),charIDToTypeID('#Ang'),A.vertical||0);executeAction(charIDToTypeID('Trnf'),d,DialogModes.NO);return 'ok';");
926
926
 
927
927
  /* ====================== FILTROS INTELIGENTES (Smart Filters) ====================== */
928
- jsxTool("convert_for_smart_filters", "Convierte la capa activa/nombrada para aplicar Filtros inteligentes (envuelve en Smart Object; mismo evento que convert_to_smart_object — 'Convert for Smart Filters' y 'Convert to Smart Object' son el mismo comando en Photoshop).", { name: S.string().optional() },
928
+ jsxTool("convert_for_smart_filters", "Converts the active/named layer to apply Smart Filters (wraps it in a Smart Object; same event as convert_to_smart_object — 'Convert for Smart Filters' and 'Convert to Smart Object' are the same command in Photoshop).", { name: S.string().optional() },
929
929
  "if(A.name)_sel(_need(A.name));executeAction(stringIDToTypeID('newPlacedLayer'),undefined,DialogModes.NO);return 'ok';");
930
- jsxTool("list_smart_filters", "Lista los Filtros inteligentes aplicados a una capa.", { name: S.string() },
930
+ jsxTool("list_smart_filters", "Lists the Smart Filters applied to a layer.", { name: S.string() },
931
931
  "_sel(_need(A.name));var d=executeActionGet(_layerRef());if(!d.hasKey(stringIDToTypeID('smartObject')))return [];var so=d.getObjectValue(stringIDToTypeID('smartObject'));if(!so.hasKey(stringIDToTypeID('filterFX')))return [];var fx=so.getList(stringIDToTypeID('filterFX'));var a=[];for(var i=0;i<fx.count;i++){var f=fx.getObjectValue(i);a.push({name:f.hasKey(stringIDToTypeID('name'))?f.getString(stringIDToTypeID('name')):'',enabled:f.hasKey(stringIDToTypeID('enabled'))?f.getBoolean(stringIDToTypeID('enabled')):true});}return a;");
932
- jsxTool("toggle_smart_filter", "Habilita/deshabilita un Filtro inteligente por índice.", { name: S.string(), index: S.number(), enabled: S.boolean() },
932
+ jsxTool("toggle_smart_filter", "Enables/disables a Smart Filter by index.", { name: S.string(), index: S.number(), enabled: S.boolean() },
933
933
  "_sel(_need(A.name));var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);d.putInteger(stringIDToTypeID('smartFilterIndex'),A.index);d.putBoolean(charIDToTypeID('enab'),!!A.enabled);executeAction(stringIDToTypeID('enableSmartFilter'),d,DialogModes.NO);return 'ok';");
934
- jsxTool("delete_smart_filter", "Elimina un Filtro inteligente por índice.", { name: S.string(), index: S.number() },
934
+ jsxTool("delete_smart_filter", "Deletes a Smart Filter by index.", { name: S.string(), index: S.number() },
935
935
  "_sel(_need(A.name));var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);d.putInteger(stringIDToTypeID('smartFilterIndex'),A.index);executeAction(stringIDToTypeID('deleteSmartFilter'),d,DialogModes.NO);return 'ok';");
936
- jsxTool("toggle_smart_filter_mask", "Habilita/deshabilita la máscara de Filtros inteligentes de una capa.", { name: S.string(), enabled: S.boolean() },
936
+ jsxTool("toggle_smart_filter_mask", "Enables/disables a layer's Smart Filters mask.", { name: S.string(), enabled: S.boolean() },
937
937
  "_sel(_need(A.name));var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);var so=new ActionDescriptor();so.putBoolean(stringIDToTypeID('smartFilterMaskEnabled'),!!A.enabled);d.putObject(charIDToTypeID('T '),charIDToTypeID('Lyr '),so);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok';");
938
938
 
939
939
  /* ====================== GUÍAS (avanzado) / VISTA / VENTANAS ====================== */
940
- jsxTool("guides_from_layer_bounds", "Crea guías a partir de los bounds de una capa.", { name: S.string() },
940
+ jsxTool("guides_from_layer_bounds", "Creates guides from a layer's bounds.", { name: S.string() },
941
941
  "var b=_need(A.name).bounds;var d=_doc();d.guides.add(Direction.VERTICAL,b[0]);d.guides.add(Direction.VERTICAL,b[2]);d.guides.add(Direction.HORIZONTAL,b[1]);d.guides.add(Direction.HORIZONTAL,b[3]);return 'ok';");
942
- jsxTool("set_snap_to_guides", "Activa/desactiva ajustar a guías.", { enabled: S.boolean() }, "app.preferences.snapToGuide=!!A.enabled;return 'ok (best-effort)';");
943
- jsxTool("toggle_extras", "Muestra/oculta Extras (guías, grilla, bordes de selección) en pantalla.", { visible: S.boolean() },
942
+ jsxTool("set_snap_to_guides", "Enables/disables snap to guides.", { enabled: S.boolean() }, "app.preferences.snapToGuide=!!A.enabled;return 'ok (best-effort)';");
943
+ jsxTool("toggle_extras", "Shows/hides Extras (guides, grid, selection edges) on screen.", { visible: S.boolean() },
944
944
  "executeAction(stringIDToTypeID(A.visible?'showExtras':'hideExtras'),undefined,DialogModes.NO);return 'ok';");
945
- jsxTool("fit_on_screen", "Ajusta el zoom para ver el documento completo.", {}, "executeAction(charIDToTypeID('FtOn'),undefined,DialogModes.NO);return 'ok';");
946
- jsxTool("set_zoom", "Zoom del documento activo (porcentaje, ej. 100).", { percent: S.number() },
947
- "var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('FtcS'),charIDToTypeID('#Prc'),A.percent);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok (best-effort, algunas builds requieren la referencia zoomView)';");
948
- jsxTool("arrange_documents", "Organiza las ventanas de documentos abiertos (mode: tile|cascade|consolidate).", { mode: S.string().optional() },
945
+ jsxTool("fit_on_screen", "Adjusts zoom to fit the whole document on screen.", {}, "executeAction(charIDToTypeID('FtOn'),undefined,DialogModes.NO);return 'ok';");
946
+ jsxTool("set_zoom", "Zoom of the active document (percentage, e.g. 100).", { percent: S.number() },
947
+ "var d=new ActionDescriptor();d.putUnitDouble(charIDToTypeID('FtcS'),charIDToTypeID('#Prc'),A.percent);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok (best-effort, some builds require the zoomView reference)';");
948
+ jsxTool("arrange_documents", "Arranges the open document windows (mode: tile|cascade|consolidate).", { mode: S.string().optional() },
949
949
  "var m=A.mode||'tile';var id=m==='cascade'?stringIDToTypeID('cascade'):(m==='consolidate'?stringIDToTypeID('consolidateAll'):stringIDToTypeID('tile'));executeAction(id,undefined,DialogModes.NO);return 'ok';");
950
950
 
951
951
  /* ====================== CUENTAGOTAS / MUESTREO (config) ====================== */
952
- jsxTool("set_eyedropper_sample_size", "Tamaño de muestra del cuentagotas (point|3x3|5x5|11x11|31x31|51x51|101x101).", { size: S.string() },
952
+ jsxTool("set_eyedropper_sample_size", "Eyedropper sample size (point|3x3|5x5|11x11|31x31|51x51|101x101).", { size: S.string() },
953
953
  "var m={point:charIDToTypeID('PntS'),'3x3':stringIDToTypeID('sample3x3'),'5x5':stringIDToTypeID('sample5x5'),'11x11':stringIDToTypeID('sample11x11'),'31x31':stringIDToTypeID('sample31x31'),'51x51':stringIDToTypeID('sample51x51'),'101x101':stringIDToTypeID('sample101x101')};var d=new ActionDescriptor();d.putEnumerated(stringIDToTypeID('sampleSize'),stringIDToTypeID('sampleSize'),m[A.size]||m.point);var r=new ActionReference();r.putEnumerated(charIDToTypeID('capp'),charIDToTypeID('Ordn'),charIDToTypeID('Trgt'));d.putReference(charIDToTypeID('null'),r);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok (best-effort)';");
954
954
 
955
955
  /* ====================== BLEND IF (mezcla por canal) ====================== */
956
- jsxTool("set_blend_if", "Configura 'Esta capa' / 'Subyacente' del panel Blend If (canal gray por defecto, 0-255).", { name: S.string().optional(), thisLayerBlack: S.number().optional(), thisLayerWhite: S.number().optional(), underlyingBlack: S.number().optional(), underlyingWhite: S.number().optional(), channel: S.string().optional() },
956
+ jsxTool("set_blend_if", "Configures 'This Layer' / 'Underlying' in the Blend If panel (gray channel by default, 0-255).", { name: S.string().optional(), thisLayerBlack: S.number().optional(), thisLayerWhite: S.number().optional(), underlyingBlack: S.number().optional(), underlyingWhite: S.number().optional(), channel: S.string().optional() },
957
957
  "if(A.name)_sel(_need(A.name));var d=new ActionDescriptor();var r=_layerRef();d.putReference(charIDToTypeID('null'),r);var bo=new ActionDescriptor();var chMap={gray:charIDToTypeID('Gry '),red:charIDToTypeID('Rd '),green:charIDToTypeID('Grn '),blue:charIDToTypeID('Bl ')};var chId=chMap[A.channel||'gray'];var g=new ActionDescriptor();g.putInteger(charIDToTypeID('Bt0 '),A.thisLayerBlack!=null?A.thisLayerBlack:0);g.putInteger(charIDToTypeID('Bt1 '),A.thisLayerBlack!=null?A.thisLayerBlack:0);g.putInteger(charIDToTypeID('Wt0 '),A.thisLayerWhite!=null?A.thisLayerWhite:255);g.putInteger(charIDToTypeID('Wt1 '),A.thisLayerWhite!=null?A.thisLayerWhite:255);bo.putObject(chId,charIDToTypeID('BlnS'),g);var gu=new ActionDescriptor();gu.putInteger(charIDToTypeID('Bt0 '),A.underlyingBlack!=null?A.underlyingBlack:0);gu.putInteger(charIDToTypeID('Bt1 '),A.underlyingBlack!=null?A.underlyingBlack:0);gu.putInteger(charIDToTypeID('Wt0 '),A.underlyingWhite!=null?A.underlyingWhite:255);gu.putInteger(charIDToTypeID('Wt1 '),A.underlyingWhite!=null?A.underlyingWhite:255);var blndKey=stringIDToTypeID('blendRangeUnderlying');bo.putObject(blndKey,charIDToTypeID('BlnS'),gu);d.putObject(charIDToTypeID('T '),charIDToTypeID('Lyr '),bo);executeAction(charIDToTypeID('setd'),d,DialogModes.NO);return 'ok (best-effort AM)';");
958
958
 
959
959
  /* ====================== LAYER VIA COPY/CUT ====================== */
960
- jsxTool("new_layer_via_copy", "Crea una capa nueva a partir de la selección (copia).", {}, "executeAction(charIDToTypeID('CpTL'),undefined,DialogModes.NO);return 'ok';");
961
- jsxTool("new_layer_via_cut", "Crea una capa nueva a partir de la selección (corte).", {}, "executeAction(charIDToTypeID('CtTL'),undefined,DialogModes.NO);return 'ok';");
960
+ jsxTool("new_layer_via_copy", "Creates a new layer from the selection (copy).", {}, "executeAction(charIDToTypeID('CpTL'),undefined,DialogModes.NO);return 'ok';");
961
+ jsxTool("new_layer_via_cut", "Creates a new layer from the selection (cut).", {}, "executeAction(charIDToTypeID('CtTL'),undefined,DialogModes.NO);return 'ok';");
962
962
 
963
963
  /* ====================== TEXTO: PÁRRAFO/CARÁCTER/OPENTYPE ====================== */
964
- jsxTool("convert_text_to_shape", "Convierte una capa de texto en capa de forma vectorial.", { name: S.string() }, "_sel(_need(A.name));executeAction(stringIDToTypeID('convertToShape'),undefined,DialogModes.NO);return 'ok';");
965
- jsxTool("convert_text_to_path", "Crea una ruta de trabajo desde los contornos de una capa de texto.", { name: S.string() }, "_sel(_need(A.name));executeAction(stringIDToTypeID('createWorkPath'),undefined,DialogModes.NO);return 'ok';");
966
- jsxTool("set_opentype_feature", "Activa/desactiva una característica OpenType (ligatures|oldStyle|fractions|ordinals|swash|titling|stylistic).", { name: S.string(), feature: S.string(), on: S.boolean() },
967
- "var t=_need(A.name).textItem;var map={ligatures:'ligature',oldStyle:'oldStyle',fractions:'fractions',ordinals:'ordinals',swash:'swash',titling:'titling',stylistic:'stylisticAlternates'};var prop=map[A.feature];if(prop&&(prop in t))t[prop]=!!A.on;return 'ok (best-effort, depende de la fuente)';");
968
- jsxTool("get_used_fonts", "Escanea el documento y lista las fuentes usadas en capas de texto.", {},
964
+ jsxTool("convert_text_to_shape", "Converts a text layer into a vector shape layer.", { name: S.string() }, "_sel(_need(A.name));executeAction(stringIDToTypeID('convertToShape'),undefined,DialogModes.NO);return 'ok';");
965
+ jsxTool("convert_text_to_path", "Creates a work path from a text layer's outlines.", { name: S.string() }, "_sel(_need(A.name));executeAction(stringIDToTypeID('createWorkPath'),undefined,DialogModes.NO);return 'ok';");
966
+ jsxTool("set_opentype_feature", "Enables/disables an OpenType feature (ligatures|oldStyle|fractions|ordinals|swash|titling|stylistic).", { name: S.string(), feature: S.string(), on: S.boolean() },
967
+ "var t=_need(A.name).textItem;var map={ligatures:'ligature',oldStyle:'oldStyle',fractions:'fractions',ordinals:'ordinals',swash:'swash',titling:'titling',stylistic:'stylisticAlternates'};var prop=map[A.feature];if(prop&&(prop in t))t[prop]=!!A.on;return 'ok (best-effort, depends on the font)';");
968
+ jsxTool("get_used_fonts", "Scans the document and lists the fonts used in text layers.", {},
969
969
  "var fonts={};function walk(ls){for(var i=0;i<ls.length;i++){var L=ls[i];try{if(L.kind===LayerKind.TEXT)fonts[L.textItem.font]=true;}catch(e){}if(L.typename==='LayerSet')walk(L.layers);}}walk(_doc().layers);var a=[];for(var k in fonts)a.push(k);return a;");
970
- jsxTool("list_text_layers", "Lista todas las capas de texto del documento (path completo, contenido, fuente, tamaño).", {},
970
+ jsxTool("list_text_layers", "Lists all text layers in the document (full path, content, font, size).", {},
971
971
  "var a=[];function walk(ls,prefix){for(var i=0;i<ls.length;i++){var L=ls[i];var path=prefix+'/'+L.name;try{if(L.kind===LayerKind.TEXT)a.push({path:path,text:String(L.textItem.contents),font:String(L.textItem.font),size:Math.round(parseFloat(L.textItem.size)*100)/100});}catch(e){}if(L.typename==='LayerSet')walk(L.layers,path);}}walk(_doc().layers,'');return a;");
972
- jsxTool("get_all_layer_paths", "Lista el path completo (con '/') de todas las capas/grupos del documento.", {},
972
+ jsxTool("get_all_layer_paths", "Lists the full path (with '/') of all layers/groups in the document.", {},
973
973
  "var a=[];function walk(ls,prefix){for(var i=0;i<ls.length;i++){var L=ls[i];var path=prefix+'/'+L.name;a.push({path:path,type:L.typename==='LayerSet'?'group':String(L.kind).replace('LayerKind.','')});if(L.typename==='LayerSet')walk(L.layers,path);}}walk(_doc().layers,'');return a;");
974
- jsxTool("find_layers_by_kind", "Busca capas por tipo (TEXT|SMARTOBJECT|SOLIDFILL|group|NORMAL...).", { kind: S.string() },
974
+ jsxTool("find_layers_by_kind", "Finds layers by type (TEXT|SMARTOBJECT|SOLIDFILL|group|NORMAL...).", { kind: S.string() },
975
975
  "var a=[];function walk(ls,prefix){for(var i=0;i<ls.length;i++){var L=ls[i];var path=prefix+'/'+L.name;var k=L.typename==='LayerSet'?'group':String(L.kind).replace('LayerKind.','');if(k.toUpperCase()===String(A.kind).toUpperCase())a.push(path);if(L.typename==='LayerSet')walk(L.layers,path);}}walk(_doc().layers,'');return a;");
976
976
 
977
977
  /* ====================== APP: DOCUMENTOS RECIENTES / MASIVO ====================== */
978
- jsxTool("get_recent_files", "Lista los archivos recientes de la app.", {}, "var a=[];try{for(var i=0;i<app.recentFiles.length;i++)a.push(app.recentFiles[i].fsName);}catch(e){}return a;");
979
- jsxTool("close_all_documents", "Cierra todos los documentos abiertos.", { save: S.boolean().optional() },
978
+ jsxTool("get_recent_files", "Lists the app's recent files.", {}, "var a=[];try{for(var i=0;i<app.recentFiles.length;i++)a.push(app.recentFiles[i].fsName);}catch(e){}return a;");
979
+ jsxTool("close_all_documents", "Closes all open documents.", { save: S.boolean().optional() },
980
980
  "while(app.documents.length)app.documents[0].close(A.save?SaveOptions.SAVECHANGES:SaveOptions.DONOTSAVECHANGES);return 'ok';");
981
- jsxTool("save_all_documents", "Guarda todos los documentos abiertos.", {}, "for(var i=0;i<app.documents.length;i++){try{app.documents[i].save();}catch(e){}}return 'ok';");
982
- jsxTool("export_document_thumbnail", "Exporta una miniatura JPG de baja resolución del documento activo.", { path: S.string(), maxSize: S.number().optional() },
981
+ jsxTool("save_all_documents", "Saves all open documents.", {}, "for(var i=0;i<app.documents.length;i++){try{app.documents[i].save();}catch(e){}}return 'ok';");
982
+ jsxTool("export_document_thumbnail", "Exports a low-resolution JPG thumbnail of the active document.", { path: S.string(), maxSize: S.number().optional() },
983
983
  "var dup=_doc().duplicate();app.activeDocument=dup;dup.flatten();var w=dup.width.as('px'),h=dup.height.as('px');var max=A.maxSize||256;var scale=Math.min(max/w,max/h,1);if(scale<1)dup.resizeImage(UnitValue(w*scale,'px'),UnitValue(h*scale,'px'));var o=new JPEGSaveOptions();o.quality=8;dup.saveAs(new File(A.path),o,true,Extension.LOWERCASE);dup.close(SaveOptions.DONOTSAVECHANGES);return {path:A.path};");
984
984
 
985
985
  /* ====================== RENDIMIENTO / GPU ====================== */
986
- jsxTool("set_gpu_acceleration", "Activa/desactiva aceleración por GPU (requiere reiniciar Photoshop para aplicar del todo).", { enabled: S.boolean() },
987
- "try{app.preferences.useGraphicsProcessor=!!A.enabled;}catch(e){}return 'ok (best-effort, puede requerir reinicio)';");
988
- jsxTool("get_gpu_info", "Info de la GPU detectada por Photoshop (best-effort).", {},
989
- "try{return {useGraphicsProcessor:app.preferences.useGraphicsProcessor};}catch(e){return {info:'no disponible'};}");
986
+ jsxTool("set_gpu_acceleration", "Enables/disables GPU acceleration (requires restarting Photoshop to fully apply).", { enabled: S.boolean() },
987
+ "try{app.preferences.useGraphicsProcessor=!!A.enabled;}catch(e){}return 'ok (best-effort, may require a restart)';");
988
+ jsxTool("get_gpu_info", "Info about the GPU detected by Photoshop (best-effort).", {},
989
+ "try{return {useGraphicsProcessor:app.preferences.useGraphicsProcessor};}catch(e){return {info:'not available'};}");
990
990
 
991
991
  /* ====================== ESTILOS DE PÁRRAFO / CARÁCTER ====================== */
992
- jsxTool("list_paragraph_styles", "Lista los estilos de párrafo del documento (si el motor de texto los expone).", {},
992
+ jsxTool("list_paragraph_styles", "Lists the document's paragraph styles (if the text engine exposes them).", {},
993
993
  "var a=[];try{var ps=_doc().paragraphStyles;for(var i=0;i<ps.length;i++)a.push(ps[i].name);}catch(e){}return a;");
994
- jsxTool("list_character_styles", "Lista los estilos de carácter del documento (si el motor de texto los expone).", {},
994
+ jsxTool("list_character_styles", "Lists the document's character styles (if the text engine exposes them).", {},
995
995
  "var a=[];try{var cs=_doc().characterStyles;for(var i=0;i<cs.length;i++)a.push(cs[i].name);}catch(e){}return a;");
996
- jsxTool("apply_paragraph_style", "Aplica un estilo de párrafo a una capa de texto por nombre.", { name: S.string(), style: S.string() },
997
- "var L=_need(A.name);try{var ps=_doc().paragraphStyles.getByName(A.style);L.textItem.paragraphStyle=ps;return 'ok';}catch(e){return 'no disponible en esta build: '+e;}");
998
- jsxTool("apply_character_style", "Aplica un estilo de carácter a una capa de texto por nombre.", { name: S.string(), style: S.string() },
999
- "var L=_need(A.name);try{var cs=_doc().characterStyles.getByName(A.style);L.textItem.characterStyle=cs;return 'ok';}catch(e){return 'no disponible en esta build: '+e;}");
996
+ jsxTool("apply_paragraph_style", "Applies a paragraph style to a text layer by name.", { name: S.string(), style: S.string() },
997
+ "var L=_need(A.name);try{var ps=_doc().paragraphStyles.getByName(A.style);L.textItem.paragraphStyle=ps;return 'ok';}catch(e){return 'not available in this build: '+e;}");
998
+ jsxTool("apply_character_style", "Applies a character style to a text layer by name.", { name: S.string(), style: S.string() },
999
+ "var L=_need(A.name);try{var cs=_doc().characterStyles.getByName(A.style);L.textItem.characterStyle=cs;return 'ok';}catch(e){return 'not available in this build: '+e;}");
1000
1000
 
1001
1001
  /* ====================== FILTROS/CORRECCIÓN (extra confiables) ====================== */
1002
- jsxTool("apply_lens_correction_auto", "Corrección de lente automática (best-effort, AM).", { name: S.string().optional() },
1003
- "if(A.name)_sel(_need(A.name));try{executeAction(stringIDToTypeID('lensCorrection'),new ActionDescriptor(),DialogModes.NO);return 'ok';}catch(e){return 'no disponible: '+e;}");
1004
- jsxTool("apply_vanishing_point_note", "Vanishing Point NO tiene API de scripting estable en Photoshop; usar la UI manualmente. Este tool solo documenta el gap.", {},
1005
- "return 'Vanishing Point no es scripteable de forma confiable. Hacelo manualmente en Filtro > Vanishing Point.';");
1002
+ jsxTool("apply_lens_correction_auto", "Automatic lens correction (best-effort, AM).", { name: S.string().optional() },
1003
+ "if(A.name)_sel(_need(A.name));try{executeAction(stringIDToTypeID('lensCorrection'),new ActionDescriptor(),DialogModes.NO);return 'ok';}catch(e){return 'not available: '+e;}");
1004
+ jsxTool("apply_vanishing_point_note", "Vanishing Point does NOT have a stable scripting API in Photoshop; use the UI manually. This tool only documents the gap.", {},
1005
+ "return 'Vanishing Point is not reliably scriptable. Do it manually in Filter > Vanishing Point.';");
1006
1006
 
1007
1007
  /* ====================== HELP / CATÁLOGO ====================== */
1008
- server.tool("ps_help", "Resumen de capacidades por categoría (usá tools/list del cliente MCP para el listado completo).", {}, async () => text(
1009
- `Photoshop MCP (Synthetic) — categorías
1008
+ server.tool("ps_help", "Summary of capabilities by category (use the MCP client's tools/list for the full listing).", {}, async () => text(
1009
+ `Photoshop MCP (Synthetic) — categories
1010
1010
 
1011
- APP/DIAGNÓSTICO: ps_healthcheck, ps_info, ps_help, run_jsx, run_jsx_json, run_action
1012
- DOCUMENTOS: list/get/create/open/save/save_as/close/set_active/duplicate/flatten/resize_image/resize_canvas/crop/trim/rotate_canvas/revert/purge/place_file/change_color_mode/change_bit_depth/reveal_all
1013
- CAPAS: list_layers, get_layer, get_layer_bounds, add_text_layer, add_color_layer, create_group, delete/duplicate/rename/select_layer, set_layer_visibility/opacity/fill_opacity/blend_mode/label_color, lock_layer, set_clipping_mask, clear_layer_pixels, translate/move_to_front/back, rasterize, merge_down/visible/group
1014
- TEXTO: set_text_contents, set_text_style, set_text_justification/tracking/leading/antialias/box
1011
+ APP/DIAGNOSTICS: ps_healthcheck, ps_info, ps_help, run_jsx, run_jsx_json, run_action
1012
+ DOCUMENTS: list/get/create/open/save/save_as/close/set_active/duplicate/flatten/resize_image/resize_canvas/crop/trim/rotate_canvas/revert/purge/place_file/change_color_mode/change_bit_depth/reveal_all
1013
+ LAYERS: list_layers, get_layer, get_layer_bounds, add_text_layer, add_color_layer, create_group, delete/duplicate/rename/select_layer, set_layer_visibility/opacity/fill_opacity/blend_mode/label_color, lock_layer, set_clipping_mask, clear_layer_pixels, translate/move_to_front/back, rasterize, merge_down/visible/group
1014
+ TEXT: set_text_contents, set_text_style, set_text_justification/tracking/leading/antialias/box
1015
1015
  COLOR: fill_layer, set_foreground_color, set_background_color, swap_colors, get_pixel_color
1016
- SELECCIÓN: select_all/deselect/select_rectangle/select_ellipse/invert/expand/contract/border/feather/smooth, fill/stroke/copy/cut/clear, paste, make_work_path, transform_selection_move, select_layer_pixels, save_selection_to_channel
1017
- CANALES: list_channels, add_alpha_channel, delete_channel, duplicate_channel
1018
- MÁSCARAS: add_layer_mask, delete_layer_mask
1016
+ SELECTION: select_all/deselect/select_rectangle/select_ellipse/invert/expand/contract/border/feather/smooth, fill/stroke/copy/cut/clear, paste, make_work_path, transform_selection_move, select_layer_pixels, save_selection_to_channel
1017
+ CHANNELS: list_channels, add_alpha_channel, delete_channel, duplicate_channel
1018
+ MASKS: add_layer_mask, delete_layer_mask
1019
1019
  TRANSFORM: scale_layer, rotate_layer, flip_layer, set_layer_position
1020
- FILTROS: gaussian_blur, blur, blur_more, sharpen(_more/_edges), unsharp_mask, smart_blur, motion_blur, radial_blur, high_pass, median, maximum, minimum, offset, add_noise, despeckle, dust_and_scratches, clouds, difference_clouds, lens_flare, diffuse_glow, ocean_ripple, pinch, spherize, twirl, ripple, zigzag, wind, polar_coordinates, solarize
1021
- AJUSTES (destructivos): adjust_brightness_contrast/levels/color_balance, desaturate/invert/posterize/threshold/equalize_layer, auto_levels/auto_contrast
1022
- CAPAS DE AJUSTE: create_adjustment_layer (brightness|levels|curves|hueSaturation|colorBalance|blackWhite|photoFilter|channelMixer|invert|posterize|threshold|selectiveColor|vibrance|exposure|gradientMap)
1023
- EFECTOS DE CAPA: add_drop_shadow, add_stroke_effect, add_color_overlay, clear_layer_effects
1020
+ FILTERS: gaussian_blur, blur, blur_more, sharpen(_more/_edges), unsharp_mask, smart_blur, motion_blur, radial_blur, high_pass, median, maximum, minimum, offset, add_noise, despeckle, dust_and_scratches, clouds, difference_clouds, lens_flare, diffuse_glow, ocean_ripple, pinch, spherize, twirl, ripple, zigzag, wind, polar_coordinates, solarize
1021
+ ADJUSTMENTS (destructive): adjust_brightness_contrast/levels/color_balance, desaturate/invert/posterize/threshold/equalize_layer, auto_levels/auto_contrast
1022
+ ADJUSTMENT LAYERS: create_adjustment_layer (brightness|levels|curves|hueSaturation|colorBalance|blackWhite|photoFilter|channelMixer|invert|posterize|threshold|selectiveColor|vibrance|exposure|gradientMap)
1023
+ LAYER EFFECTS: add_drop_shadow, add_stroke_effect, add_color_overlay, clear_layer_effects
1024
1024
  SMART OBJECTS: replace_smart_object, convert_to_smart_object, rasterize_smart_object
1025
- GUÍAS: add_guide, list_guides, clear_guides
1026
- HISTORIA: undo, create_snapshot
1027
- ACCIONES: play_action
1025
+ GUIDES: add_guide, list_guides, clear_guides
1026
+ HISTORY: undo, create_snapshot
1027
+ ACTIONS: play_action
1028
1028
  EXPORT: export_png, export_jpg, export_layer_png, save_for_web_png, export_tiff, export_pdf, export_bmp, export_targa, export_gif, export_jpg_sfw
1029
- DOC/METADATA/HISTORIA: get/set_document_metadata, get_xmp_metadata, get_histogram, resize_image_resample, list_history_states, set_history_state, redo, step_backward, count_items
1030
- APP/FUENTES: list_fonts, set_ruler_units, get_system_info
1031
- CAPAS (grupos/orden): select_layers, select_all_layers, deselect_layers, group_selected_layers, ungroup_layers, reorder_layer, move_layer_into_group, link_selected_layers, unlink_layer, align_layers, distribute_layers, convert_background_to_layer, get_layer_id
1032
- TEXTO (más): set_text_baseline_shift, faux_bold/italic, underline, all_caps, hyphenation, autoleading, warp_text, set_font_by_postscript
1033
- SELECCIÓN (más): select_subject, select_sky, select_color_range, grow_selection, similar_selection, load_channel_as_selection, select_polygon, magic_wand_select, get_selection_bounds
1034
- MÁSCARAS (más): apply_layer_mask, toggle_layer_mask, invert_layer_mask, mask_from_selection, add_vector_mask
1035
- CANALES (más): split_channels, set_active_channels, channel_to_selection
1036
- RUTAS: list_paths, delete_path, duplicate_path, path_to_selection, fill_path, stroke_path, create_path_from_points
1037
- FORMAS VECTORIALES: add_shape_rectangle, add_shape_rounded_rectangle, add_shape_ellipse
1038
- TRANSFORM (más): align_layer_to_canvas, free_transform_layer, flip_canvas
1039
- ESTILOS DE CAPA (más): add_inner_shadow, add_outer_glow, add_inner_glow, add_satin, add_bevel_emboss, add_gradient_overlay, get_layer_effects, apply_style_by_name, scale_layer_effects
1040
- AJUSTES CON PARÁMETROS: adjust_curves, adjust_hue_saturation, adjust_vibrance, adjust_exposure, adjust_photo_filter, adjust_shadow_highlight, black_and_white
1041
- CAPAS DE AJUSTE (param): add_adjustment_brightness_contrast/hue_saturation/vibrance/exposure/photo_filter/black_white/gradient_map, add_solid_color_fill, add_gradient_fill
1042
- FILTROS (más): apply_average, grain, ntsc, deinterlace, wave, mosaic, crystallize, pointillize, color_halftone, facet, fragment, find_edges, emboss, smart_sharpen, surface_blur, box_blur
1043
- SMART OBJECTS (más): new_smart_object_via_copy, export_smart_object_contents, reset_smart_object_transform
1044
- COLOR/MUESTREO: sample_region_average_color, add_color_sampler, list_color_samplers
1029
+ DOC/METADATA/HISTORY: get/set_document_metadata, get_xmp_metadata, get_histogram, resize_image_resample, list_history_states, set_history_state, redo, step_backward, count_items
1030
+ APP/FONTS: list_fonts, set_ruler_units, get_system_info
1031
+ LAYERS (groups/order): select_layers, select_all_layers, deselect_layers, group_selected_layers, ungroup_layers, reorder_layer, move_layer_into_group, link_selected_layers, unlink_layer, align_layers, distribute_layers, convert_background_to_layer, get_layer_id
1032
+ TEXT (more): set_text_baseline_shift, faux_bold/italic, underline, all_caps, hyphenation, autoleading, warp_text, set_font_by_postscript
1033
+ SELECTION (more): select_subject, select_sky, select_color_range, grow_selection, similar_selection, load_channel_as_selection, select_polygon, magic_wand_select, get_selection_bounds
1034
+ MASKS (more): apply_layer_mask, toggle_layer_mask, invert_layer_mask, mask_from_selection, add_vector_mask
1035
+ CHANNELS (more): split_channels, set_active_channels, channel_to_selection
1036
+ PATHS: list_paths, delete_path, duplicate_path, path_to_selection, fill_path, stroke_path, create_path_from_points
1037
+ VECTOR SHAPES: add_shape_rectangle, add_shape_rounded_rectangle, add_shape_ellipse
1038
+ TRANSFORM (more): align_layer_to_canvas, free_transform_layer, flip_canvas
1039
+ LAYER STYLES (more): add_inner_shadow, add_outer_glow, add_inner_glow, add_satin, add_bevel_emboss, add_gradient_overlay, get_layer_effects, apply_style_by_name, scale_layer_effects
1040
+ ADJUSTMENTS WITH PARAMETERS: adjust_curves, adjust_hue_saturation, adjust_vibrance, adjust_exposure, adjust_photo_filter, adjust_shadow_highlight, black_and_white
1041
+ ADJUSTMENT LAYERS (parameterized): add_adjustment_brightness_contrast/hue_saturation/vibrance/exposure/photo_filter/black_white/gradient_map, add_solid_color_fill, add_gradient_fill
1042
+ FILTERS (more): apply_average, grain, ntsc, deinterlace, wave, mosaic, crystallize, pointillize, color_halftone, facet, fragment, find_edges, emboss, smart_sharpen, surface_blur, box_blur
1043
+ SMART OBJECTS (more): new_smart_object_via_copy, export_smart_object_contents, reset_smart_object_transform
1044
+ COLOR/SAMPLING: sample_region_average_color, add_color_sampler, list_color_samplers
1045
1045
  BATCH: batch_rename_layers, batch_set_visibility, batch_translate_layers, batch_export_layers_png
1046
- GUÍAS (más): delete_guide, add_guides_grid, guides_from_layer_bounds
1046
+ GUIDES (more): delete_guide, add_guides_grid, guides_from_layer_bounds
1047
1047
 
1048
1048
  LAYER COMPS: list/create/apply/update/delete_layer_comp, restore_last_document_state
1049
- ACCIONES (gestión): list_action_sets, load_action_set, delete_action(_set), create_action_set, record_action_start
1050
- COLOR SETTINGS/PERFILES: get/assign/remove_color_profile, convert_to_profile
1049
+ ACTIONS (management): list_action_sets, load_action_set, delete_action(_set), create_action_set, record_action_start
1050
+ COLOR SETTINGS/PROFILES: get/assign/remove_color_profile, convert_to_profile
1051
1051
  PRESETS DOC/PRINT: create_document_from_preset, print_document, print_one_copy
1052
- CONTENIDO/RECOMPOSICIÓN: content_aware_fill/scale/move, perspective_warp, puppet_warp_commit, auto_align_layers, auto_blend_layers, merge_to_hdr, load_files_into_stack, refine_selection_edge
1052
+ CONTENT-AWARE/RECOMPOSITION: content_aware_fill/scale/move, perspective_warp, puppet_warp_commit, auto_align_layers, auto_blend_layers, merge_to_hdr, load_files_into_stack, refine_selection_edge
1053
1053
  SLICES: list/add/delete_slice, export_slices
1054
1054
  ARTBOARDS: create/resize/list_artboards
1055
- DATA SETS (gráficos con datos): define_text_variable, define_visibility_variable, import_data_sets_from_csv, apply_data_set, export_data_sets_as_files
1056
- 3D (básico): new_3d_layer_from_file, render_3d_layer, merge_3d_layer
1055
+ DATA SETS (data-driven graphics): define_text_variable, define_visibility_variable, import_data_sets_from_csv, apply_data_set, export_data_sets_as_files
1056
+ 3D (basic): new_3d_layer_from_file, render_3d_layer, merge_3d_layer
1057
1057
  VIDEO/TIMELINE: set_timeline_current_time, add_video_keyframe, set_video_duration, render_video_to_file
1058
1058
  CAMERA RAW: apply_camera_raw_filter, open_raw_with_settings
1059
- PRESETS (patrones/muestras/pinceles): define_pattern_from_selection, define_custom_shape_from_path, add_swatch, save_gradient_preset, set_brush_preset, set_brush_size
1060
- PINTURA: paint_stroke, eraser_stroke, clone_stamp_stroke, gradient_tool_apply
1061
- PREFERENCIAS: get_preferences, set_type_units, set_interpolation_method, set_history_states_count, set_grid_preferences, get_scratch_disks, set_color_picker_hue
1062
- BATCH SOBRE CARPETA: batch_run_action_on_folder, image_processor_folder, run_jsx_on_folder
1063
- NOTAS: add_note, list_notes, delete_all_notes
1059
+ PRESETS (patterns/swatches/brushes): define_pattern_from_selection, define_custom_shape_from_path, add_swatch, save_gradient_preset, set_brush_preset, set_brush_size
1060
+ PAINTING: paint_stroke, eraser_stroke, clone_stamp_stroke, gradient_tool_apply
1061
+ PREFERENCES: get_preferences, set_type_units, set_interpolation_method, set_history_states_count, set_grid_preferences, get_scratch_disks, set_color_picker_hue
1062
+ BATCH OVER FOLDER: batch_run_action_on_folder, image_processor_folder, run_jsx_on_folder
1063
+ NOTES: add_note, list_notes, delete_all_notes
1064
1064
  FRAME TOOL: create_frame_from_shape, place_into_frame
1065
- TEXTO EN RUTA / MÁS FORMAS: add_text_on_path, add_shape_polygon, add_shape_line, boolean_path_operation
1066
- CAPAS DE AJUSTE (avanzadas): add_adjustment_selective_color, channel_mixer, curves_points, levels_params, color_balance, invert, posterize, threshold
1067
- EXPORT MODERNO: quick_export_layer, export_layer_multi_size
1068
- COLOR CRUZADO: match_color, replace_color
1069
- TRANSFORM LIBRE: transform_layer_corners, skew_layer_precise
1065
+ TEXT ON PATH / MORE SHAPES: add_text_on_path, add_shape_polygon, add_shape_line, boolean_path_operation
1066
+ ADJUSTMENT LAYERS (advanced): add_adjustment_selective_color, channel_mixer, curves_points, levels_params, color_balance, invert, posterize, threshold
1067
+ MODERN EXPORT: quick_export_layer, export_layer_multi_size
1068
+ CROSS-DOCUMENT COLOR: match_color, replace_color
1069
+ FREE TRANSFORM: transform_layer_corners, skew_layer_precise
1070
1070
  SMART FILTERS: convert_for_smart_filters, list/toggle/delete_smart_filter, toggle_smart_filter_mask
1071
- VISTA/VENTANAS: set_snap_to_guides, toggle_extras, fit_on_screen, set_zoom, arrange_documents, set_eyedropper_sample_size
1071
+ VIEW/WINDOWS: set_snap_to_guides, toggle_extras, fit_on_screen, set_zoom, arrange_documents, set_eyedropper_sample_size
1072
1072
  BLEND IF: set_blend_if
1073
1073
  LAYER VIA COPY/CUT: new_layer_via_copy/cut
1074
- TEXTO (utilidades): convert_text_to_shape/path, set_opentype_feature, get_used_fonts, list_text_layers, get_all_layer_paths, find_layers_by_kind
1075
- APP (recientes/masivo): get_recent_files, close_all_documents, save_all_documents, export_document_thumbnail
1076
- RENDIMIENTO/GPU: set_gpu_acceleration, get_gpu_info
1077
-
1078
- GAPS CONOCIDOS (verificado en vivo contra Photoshop 27.9.1, 2026-09-05, 3 rondas de smoke test + auditoría):
1079
- - CONFIRMADO ROTO (API removida de Photoshop moderno): list_slices, add_slice, delete_slice, export_slices — 'document.slices' y 'SliceType' no existen (typeof da 'undefined').
1080
- - CONFIRMADO ROTO (referencia AM al pincel de trabajo mal construida, sin fix encontrado): set_brush_size, set_brush_preset. Las tools de pintura (paint_stroke/eraser_stroke/clone_stamp_stroke/add_shape_line) ya NO intentan setear tamaño de pincel por este camino — usan el tamaño/pincel actual de Photoshop.
1081
- - NO VERIFICADO (comando reconocido pero "no disponible", sin la estructura de descriptor correcta): grow_selection, similar_selection, create_action_set, delete_action_set.
1082
- - NO VERIFICADO / probablemente removido: define_text_variable, define_visibility_variable, import/apply/export_data_sets_* (Variables/Data Sets, heredado de ImageReady) — la propiedad AM 'dataSets' no responde.
1083
- - NO VERIFICADO (sin evento de Action Manager simple conocido, a diferencia de content_aware_fill que SÍ funciona confirmado): content_aware_scale, content_aware_move.
1084
- - add_adjustment_curves_points / add_adjustment_levels_params / add_adjustment_color_balance: crean la capa de ajuste con valores POR DEFECTO (confirmado funcionando) pero NO aplican los parámetros custom — la estructura de puntos/rangos vía AM no se pudo resolver. Para valores específicos ya, usar las versiones DESTRUCTIVAS (adjust_curves/adjust_levels/adjust_color_balance, DOM, confirmadas funcionando).
1085
- - add_shape_line: funciona pero es un RASTER (PathItem.strokePath), no una capa de forma vectorial real — el evento AM para "capa de forma línea" no existe con un ID confiable.
1086
- - Sin API de scripting en absoluto (hacerlo manual en PS): Vanishing Point, edición de malla de Liquify/Puppet Warp punto por punto, la mayoría de los filtros artísticos del Filter Gallery (Chalk & Charcoal, Cutout, Watercolor, etc.).
1087
- - CONFIRMADO FUNCIONANDO en smoke test en vivo (~84 casos individuales entre 3 rondas: layer comps, estilos de capa, ajustes con parámetros y por defecto, formas vectoriales, máscaras, texto avanzado, grupos, rutas, histograma/metadata, muestreo, content-aware fill, auto-blend, refine edge, smart filters, blend if, batch export, quick export, artboard, layer-via-copy, perfiles de color, match_color, frame tool, texto en ruta, pintura, notas, patrones, select_ellipse, magic_wand_select, save_selection_to_channel): la gran mayoría de las 409 tools están verificadas directamente o son del mismo patrón DOM/AM que una tool ya verificada.
1088
-
1089
- Cualquier cosa no nombrada → run_jsx (ExtendScript) o run_action (Action Manager).`));
1074
+ TEXT (utilities): convert_text_to_shape/path, set_opentype_feature, get_used_fonts, list_text_layers, get_all_layer_paths, find_layers_by_kind
1075
+ APP (recent/bulk): get_recent_files, close_all_documents, save_all_documents, export_document_thumbnail
1076
+ PERFORMANCE/GPU: set_gpu_acceleration, get_gpu_info
1077
+
1078
+ KNOWN GAPS (verified live against Photoshop 27.9.1, 2026-09-05, 3 rounds of smoke testing + audit):
1079
+ - CONFIRMED BROKEN (API removed from modern Photoshop): list_slices, add_slice, delete_slice, export_slices — 'document.slices' and 'SliceType' don't exist (typeof gives 'undefined').
1080
+ - CONFIRMED BROKEN (the AM reference to the working brush is built incorrectly, no fix found): set_brush_size, set_brush_preset. The painting tools (paint_stroke/eraser_stroke/clone_stamp_stroke/add_shape_line) no longer try to set the brush size this way — they use Photoshop's current brush/size.
1081
+ - UNVERIFIED (command recognized but "unavailable", without the correct descriptor structure): grow_selection, similar_selection, create_action_set, delete_action_set.
1082
+ - UNVERIFIED / likely removed: define_text_variable, define_visibility_variable, import/apply/export_data_sets_* (Variables/Data Sets, inherited from ImageReady) — the AM 'dataSets' property doesn't respond.
1083
+ - UNVERIFIED (no known simple Action Manager event, unlike content_aware_fill which IS confirmed working): content_aware_scale, content_aware_move.
1084
+ - add_adjustment_curves_points / add_adjustment_levels_params / add_adjustment_color_balance: create the adjustment layer with DEFAULT values (confirmed working) but do NOT apply custom parameters — the points/ranges structure via AM couldn't be resolved. For specific values right away, use the DESTRUCTIVE versions (adjust_curves/adjust_levels/adjust_color_balance, DOM, confirmed working).
1085
+ - add_shape_line: works but is a RASTER (PathItem.strokePath), not a real vector shape layer — the AM event for a "line shape layer" doesn't exist with a reliable ID.
1086
+ - No scripting API at all (do it manually in PS): Vanishing Point, point-by-point Liquify/Puppet Warp mesh editing, most of the Filter Gallery's artistic filters (Chalk & Charcoal, Cutout, Watercolor, etc.).
1087
+ - CONFIRMED WORKING in live smoke testing (~84 individual cases across 3 rounds: layer comps, layer styles, adjustments with parameters and defaults, vector shapes, masks, advanced text, groups, paths, histogram/metadata, sampling, content-aware fill, auto-blend, refine edge, smart filters, blend if, batch export, quick export, artboard, layer-via-copy, color profiles, match_color, frame tool, text on path, painting, notes, patterns, select_ellipse, magic_wand_select, save_selection_to_channel): the vast majority of the 409 tools are either directly verified or follow the same DOM/AM pattern as an already-verified tool.
1088
+
1089
+ Anything not listed → run_jsx (ExtendScript) or run_action (Action Manager).`));
1090
1090
 
1091
1091
  // ── CLI: --doctor ────────────────────────────────────────────────────────
1092
1092
  // Same pattern as ae-mcp/premiere-mcp: a standalone diagnostic run outside