indesign-cli 0.2.0__py3-none-any.whl

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 (72) hide show
  1. cli_anything/indesign/README.md +32 -0
  2. cli_anything/indesign/__init__.py +1 -0
  3. cli_anything/indesign/__main__.py +5 -0
  4. cli_anything/indesign/core/artifacts.py +57 -0
  5. cli_anything/indesign/core/catalog.py +405 -0
  6. cli_anything/indesign/core/domains.py +178 -0
  7. cli_anything/indesign/core/envelope.py +65 -0
  8. cli_anything/indesign/core/errors.py +30 -0
  9. cli_anything/indesign/core/health.py +46 -0
  10. cli_anything/indesign/core/hidden_backend.py +116 -0
  11. cli_anything/indesign/core/hidden_handler_schemas.py +223 -0
  12. cli_anything/indesign/core/mcp_backend.py +152 -0
  13. cli_anything/indesign/core/node_setup.py +35 -0
  14. cli_anything/indesign/core/paths.py +41 -0
  15. cli_anything/indesign/core/plugins/__init__.py +2 -0
  16. cli_anything/indesign/core/plugins/backend.py +90 -0
  17. cli_anything/indesign/core/plugins/discovery.py +69 -0
  18. cli_anything/indesign/core/plugins/host_actions.py +76 -0
  19. cli_anything/indesign/core/plugins/install.py +38 -0
  20. cli_anything/indesign/core/plugins/manifest.py +279 -0
  21. cli_anything/indesign/core/plugins/validate.py +181 -0
  22. cli_anything/indesign/core/router.py +217 -0
  23. cli_anything/indesign/core/runtime.py +59 -0
  24. cli_anything/indesign/core/scripts.py +44 -0
  25. cli_anything/indesign/core/session.py +68 -0
  26. cli_anything/indesign/indesign_cli.py +320 -0
  27. cli_anything/indesign/node/hidden_handler_bridge.mjs +111 -0
  28. cli_anything/indesign/server/package-lock.json +168 -0
  29. cli_anything/indesign/server/package.json +45 -0
  30. cli_anything/indesign/server/src/advanced/index.js +76 -0
  31. cli_anything/indesign/server/src/core/InDesignMCPServer.js +273 -0
  32. cli_anything/indesign/server/src/core/scriptExecutor.js +271 -0
  33. cli_anything/indesign/server/src/core/sessionManager.js +545 -0
  34. cli_anything/indesign/server/src/handlers/advancedTemplateHandlers.js +1072 -0
  35. cli_anything/indesign/server/src/handlers/bookHandlers.js +490 -0
  36. cli_anything/indesign/server/src/handlers/documentHandlers.js +1472 -0
  37. cli_anything/indesign/server/src/handlers/exportHandlers.js +208 -0
  38. cli_anything/indesign/server/src/handlers/graphicsHandlers.js +605 -0
  39. cli_anything/indesign/server/src/handlers/groupHandlers.js +358 -0
  40. cli_anything/indesign/server/src/handlers/helpHandlers.js +347 -0
  41. cli_anything/indesign/server/src/handlers/index.js +77 -0
  42. cli_anything/indesign/server/src/handlers/layerHandlers.js +75 -0
  43. cli_anything/indesign/server/src/handlers/masterSpreadHandlers.js +451 -0
  44. cli_anything/indesign/server/src/handlers/pageHandlers.js +698 -0
  45. cli_anything/indesign/server/src/handlers/pageItemHandlers.js +704 -0
  46. cli_anything/indesign/server/src/handlers/presentationHandlers.js +220 -0
  47. cli_anything/indesign/server/src/handlers/spreadHandlers.js +348 -0
  48. cli_anything/indesign/server/src/handlers/styleHandlers.js +458 -0
  49. cli_anything/indesign/server/src/handlers/textHandlers.js +431 -0
  50. cli_anything/indesign/server/src/handlers/utilityHandlers.js +83 -0
  51. cli_anything/indesign/server/src/index.js +17 -0
  52. cli_anything/indesign/server/src/types/index.js +106 -0
  53. cli_anything/indesign/server/src/types/toolDefinitionsAdvancedTemplates.js +144 -0
  54. cli_anything/indesign/server/src/types/toolDefinitionsBook.js +224 -0
  55. cli_anything/indesign/server/src/types/toolDefinitionsContent.js +353 -0
  56. cli_anything/indesign/server/src/types/toolDefinitionsDocument.js +409 -0
  57. cli_anything/indesign/server/src/types/toolDefinitionsExport.js +65 -0
  58. cli_anything/indesign/server/src/types/toolDefinitionsLayer.js +40 -0
  59. cli_anything/indesign/server/src/types/toolDefinitionsMasterSpread.js +160 -0
  60. cli_anything/indesign/server/src/types/toolDefinitionsPage.js +271 -0
  61. cli_anything/indesign/server/src/types/toolDefinitionsPageItemGroup.js +437 -0
  62. cli_anything/indesign/server/src/types/toolDefinitionsPresentation.js +83 -0
  63. cli_anything/indesign/server/src/types/toolDefinitionsSpread.js +158 -0
  64. cli_anything/indesign/server/src/types/toolDefinitionsUtility.js +40 -0
  65. cli_anything/indesign/server/src/utils/stringUtils.js +107 -0
  66. cli_anything/indesign/skills/SKILL.md +198 -0
  67. indesign_cli-0.2.0.dist-info/METADATA +267 -0
  68. indesign_cli-0.2.0.dist-info/RECORD +72 -0
  69. indesign_cli-0.2.0.dist-info/WHEEL +5 -0
  70. indesign_cli-0.2.0.dist-info/entry_points.txt +3 -0
  71. indesign_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
  72. indesign_cli-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,458 @@
1
+ /**
2
+ * Style management handlers
3
+ */
4
+ import { ScriptExecutor } from '../core/scriptExecutor.js';
5
+ import { formatResponse, escapeJsxString } from '../utils/stringUtils.js';
6
+
7
+ export class StyleHandlers {
8
+ /**
9
+ * Create a paragraph style
10
+ */
11
+ static async createParagraphStyle(args) {
12
+ const {
13
+ name,
14
+ fontFamily = 'Arial\\tRegular',
15
+ fontSize = 12,
16
+ textColor = 'Black',
17
+ alignment = 'LEFT_ALIGN',
18
+ leading,
19
+ spaceBefore,
20
+ spaceAfter
21
+ } = args;
22
+
23
+ const escapedName = escapeJsxString(name);
24
+ const escapedFontFamily = escapeJsxString(fontFamily);
25
+ const sanitizedTextColor = typeof textColor === 'string' && textColor.trim() !== '' ? textColor.trim() : 'Black';
26
+ const escapedTextColor = escapeJsxString(sanitizedTextColor);
27
+ const alignmentOptions = new Set(['LEFT_ALIGN', 'CENTER_ALIGN', 'RIGHT_ALIGN', 'JUSTIFY', 'FULLY_JUSTIFIED']);
28
+ const normalizedAlignment = typeof alignment === 'string' ? alignment.trim().toUpperCase() : 'LEFT_ALIGN';
29
+ const safeAlignment = alignmentOptions.has(normalizedAlignment) ? normalizedAlignment : 'LEFT_ALIGN';
30
+ const escapedAlignment = escapeJsxString(safeAlignment);
31
+
32
+ const script = [
33
+ 'if (app.documents.length === 0) {',
34
+ ' "No document open";',
35
+ '} else {',
36
+ ' var doc = app.activeDocument;',
37
+ '',
38
+ ' try {',
39
+ ` var style = doc.paragraphStyles.add({name: "${escapedName}"});`,
40
+ '',
41
+ ' // Apply font settings',
42
+ ' try {',
43
+ ` style.appliedFont = app.fonts.itemByName("${escapedFontFamily}");`,
44
+ ' } catch (fontError) {',
45
+ ' // Keep the document default font when the requested font is unavailable.',
46
+ ' }',
47
+ ` style.pointSize = ${fontSize};`,
48
+ '',
49
+ ' // Apply color',
50
+ ` if ("${escapedTextColor}" !== "Black") {`,
51
+ ' try {',
52
+ ` var color = doc.colors.itemByName("${escapedTextColor}");`,
53
+ ' if (color.isValid) {',
54
+ ' style.fillColor = color;',
55
+ ' }',
56
+ ' } catch (colorError) {',
57
+ ' // Use default color if specified color not found',
58
+ ' }',
59
+ ' }',
60
+ '',
61
+ ' // Apply alignment',
62
+ ` if ("${escapedAlignment}" === "CENTER_ALIGN") {`,
63
+ ' style.justification = Justification.CENTER_ALIGN;',
64
+ ` } else if ("${escapedAlignment}" === "RIGHT_ALIGN") {`,
65
+ ' style.justification = Justification.RIGHT_ALIGN;',
66
+ ` } else if ("${escapedAlignment}" === "JUSTIFY" || "${escapedAlignment}" === "FULLY_JUSTIFIED") {`,
67
+ ' style.justification = Justification.FULLY_JUSTIFIED;',
68
+ ' } else {',
69
+ ' style.justification = Justification.LEFT_ALIGN;',
70
+ ' }',
71
+ '',
72
+ ...(leading ? [` style.leading = ${leading};`] : []),
73
+ ...(spaceBefore ? [` style.spaceBefore = ${spaceBefore};`] : []),
74
+ ...(spaceAfter ? [` style.spaceAfter = ${spaceAfter};`] : []),
75
+ '',
76
+ ' // Verify the style was created correctly',
77
+ ' var actualFont = style.appliedFont.name;',
78
+ ' var actualSize = style.pointSize;',
79
+ ' var actualAlignment = style.justification;',
80
+ '',
81
+ ` "Paragraph style '${escapedName}' created successfully with font: " + actualFont + " at " + actualSize + "pt";`,
82
+ ' } catch (error) {',
83
+ ' "Error creating paragraph style: " + error.message;',
84
+ ' }',
85
+ '}'
86
+ ].join('\n');
87
+
88
+ const result = await ScriptExecutor.executeInDesignScript(script);
89
+ return formatResponse(result, "Create Paragraph Style");
90
+ }
91
+
92
+ /**
93
+ * Create a character style
94
+ */
95
+ static async createCharacterStyle(args) {
96
+ const {
97
+ name,
98
+ fontFamily = 'Arial\\tRegular',
99
+ fontSize = 12,
100
+ textColor = 'Black',
101
+ bold = false,
102
+ italic = false,
103
+ underline = false
104
+ } = args;
105
+
106
+ const escapedName = escapeJsxString(name);
107
+ const escapedFontFamily = escapeJsxString(fontFamily);
108
+ const sanitizedTextColor = typeof textColor === 'string' && textColor.trim() !== '' ? textColor.trim() : 'Black';
109
+ const escapedTextColor = escapeJsxString(sanitizedTextColor);
110
+
111
+ const script = [
112
+ 'if (app.documents.length === 0) {',
113
+ ' "No document open";',
114
+ '} else {',
115
+ ' var doc = app.activeDocument;',
116
+ '',
117
+ ' try {',
118
+ ` var style = doc.characterStyles.add({name: "${escapedName}"});`,
119
+ '',
120
+ ' // Apply font settings',
121
+ ' try {',
122
+ ` style.appliedFont = app.fonts.itemByName("${escapedFontFamily}");`,
123
+ ' } catch (fontError) {',
124
+ ' // Keep the document default font when the requested font is unavailable.',
125
+ ' }',
126
+ ` style.pointSize = ${fontSize};`,
127
+ '',
128
+ ' // Apply color',
129
+ ` if ("${escapedTextColor}" !== "Black") {`,
130
+ ' try {',
131
+ ` var color = doc.colors.itemByName("${escapedTextColor}");`,
132
+ ' if (color.isValid) {',
133
+ ' style.fillColor = color;',
134
+ ' }',
135
+ ' } catch (colorError) {',
136
+ ' // Use default color if specified color not found',
137
+ ' }',
138
+ ' }',
139
+ '',
140
+ ' // Apply text attributes',
141
+ ` style.fontStyle = "${bold ? 'Bold' : 'Normal'}";`,
142
+ ` style.underline = ${underline};`,
143
+ ` style.underlineOffset = ${underline ? 1 : 0};`,
144
+ ` style.underlineWeight = ${underline ? 1 : 0};`,
145
+ '',
146
+ ' // Verify the style was created correctly',
147
+ ' var actualFont = style.appliedFont.name;',
148
+ ' var actualSize = style.pointSize;',
149
+ ' var actualStyle = style.fontStyle;',
150
+ '',
151
+ ` "Character style '${escapedName}' created successfully with font: " + actualFont + " at " + actualSize + "pt";`,
152
+ ' } catch (error) {',
153
+ ' "Error creating character style: " + error.message;',
154
+ ' }',
155
+ '}'
156
+ ].join('\n');
157
+
158
+ const result = await ScriptExecutor.executeInDesignScript(script);
159
+ return formatResponse(result, "Create Character Style");
160
+ }
161
+
162
+ /**
163
+ * Apply a paragraph style to text
164
+ */
165
+ static async applyParagraphStyle(args) {
166
+ const { styleName, frameIndex = 0 } = args;
167
+ const escapedStyleName = escapeJsxString(styleName);
168
+
169
+ const script = [
170
+ 'if (app.documents.length === 0) {',
171
+ ' "No document open";',
172
+ '} else {',
173
+ ' var doc = app.activeDocument;',
174
+ ' var page = doc.pages[0];',
175
+ '',
176
+ ' try {',
177
+ ` if (${frameIndex} >= page.textFrames.length) {`,
178
+ ' "Text frame index out of range";',
179
+ ' } else {',
180
+ ` var textFrame = page.textFrames[${frameIndex}];`,
181
+ ` var style = doc.paragraphStyles.itemByName("${escapedStyleName}");`,
182
+ '',
183
+ ' if (style.isValid) {',
184
+ ' textFrame.paragraphs[0].appliedParagraphStyle = style;',
185
+ ` "Paragraph style '${escapedStyleName}' applied successfully";`,
186
+ ' } else {',
187
+ ` "Paragraph style '${escapedStyleName}' not found";`,
188
+ ' }',
189
+ ' }',
190
+ ' } catch (error) {',
191
+ ' "Error applying paragraph style: " + error.message;',
192
+ ' }',
193
+ '}'
194
+ ].join('\n');
195
+
196
+ const result = await ScriptExecutor.executeInDesignScript(script);
197
+ return formatResponse(result, "Apply Paragraph Style");
198
+ }
199
+
200
+ /**
201
+ * Apply a character style to text
202
+ */
203
+ static async applyCharacterStyle(args) {
204
+ const { styleName, frameIndex = 0, startIndex = 0, endIndex = -1 } = args;
205
+ const escapedStyleName = escapeJsxString(styleName);
206
+
207
+ const script = [
208
+ 'if (app.documents.length === 0) {',
209
+ ' "No document open";',
210
+ '} else {',
211
+ ' var doc = app.activeDocument;',
212
+ ' var page = doc.pages[0];',
213
+ '',
214
+ ' try {',
215
+ ` if (${frameIndex} >= page.textFrames.length) {`,
216
+ ' "Text frame index out of range";',
217
+ ' } else {',
218
+ ` var textFrame = page.textFrames[${frameIndex}];`,
219
+ ` var style = doc.characterStyles.itemByName("${escapedStyleName}");`,
220
+ '',
221
+ ' if (style.isValid) {',
222
+ ` if (${endIndex} === -1) {`,
223
+ ' // Apply to entire text frame',
224
+ ' textFrame.texts[0].appliedCharacterStyle = style;',
225
+ ` } else {`,
226
+ ' // Apply to specific range',
227
+ ` textFrame.texts[0].characters.itemByRange(${startIndex}, ${endIndex}).appliedCharacterStyle = style;`,
228
+ ' }',
229
+ ` "Character style '${escapedStyleName}' applied successfully";`,
230
+ ' } else {',
231
+ ` "Character style '${escapedStyleName}' not found";`,
232
+ ' }',
233
+ ' }',
234
+ ' } catch (error) {',
235
+ ' "Error applying character style: " + error.message;',
236
+ ' }',
237
+ '}'
238
+ ].join('\n');
239
+
240
+ const result = await ScriptExecutor.executeInDesignScript(script);
241
+ return formatResponse(result, "Apply Character Style");
242
+ }
243
+
244
+ /**
245
+ * List all styles in the document
246
+ */
247
+ static async listStyles(args) {
248
+ const { styleType = 'ALL' } = args;
249
+ const allowedStyleTypes = new Set(['PARAGRAPH', 'CHARACTER', 'ALL']);
250
+ const normalizedStyleType = typeof styleType === 'string' ? styleType.trim().toUpperCase() : 'ALL';
251
+ const safeStyleType = allowedStyleTypes.has(normalizedStyleType) ? normalizedStyleType : 'ALL';
252
+ const escapedStyleType = escapeJsxString(safeStyleType);
253
+
254
+ const script = [
255
+ 'if (app.documents.length === 0) {',
256
+ ' "No document open";',
257
+ '} else {',
258
+ ' var doc = app.activeDocument;',
259
+ ' var info = "=== STYLES LIST ===\\n";',
260
+ '',
261
+ ' // Helper function to get alignment name',
262
+ ' function getAlignmentName(alignment) {',
263
+ ' if (alignment === Justification.LEFT_ALIGN) return "LEFT_ALIGN";',
264
+ ' if (alignment === Justification.CENTER_ALIGN) return "CENTER_ALIGN";',
265
+ ' if (alignment === Justification.RIGHT_ALIGN) return "RIGHT_ALIGN";',
266
+ ' if (alignment === Justification.FULLY_JUSTIFIED) return "FULLY_JUSTIFIED";',
267
+ ' return "UNKNOWN (" + alignment + ")";',
268
+ ' }',
269
+ '',
270
+ ` if ("${escapedStyleType}" === "PARAGRAPH" || "${escapedStyleType}" === "ALL") {`,
271
+ ' info += "\\n=== PARAGRAPH STYLES ===\\n";',
272
+ ' for (var i = 0; i < doc.paragraphStyles.length; i++) {',
273
+ ' var style = doc.paragraphStyles[i];',
274
+ ' if (style.isValid) {',
275
+ ' info += "Name: " + style.name + "\\n";',
276
+ ' if (style.appliedFont && style.appliedFont.isValid) {',
277
+ ' info += " Font: " + style.appliedFont.name + "\\n";',
278
+ ' } else {',
279
+ ' info += " Font: [Not set]\\n";',
280
+ ' }',
281
+ ' info += " Size: " + style.pointSize + "pt\\n";',
282
+ ' info += " Alignment: " + getAlignmentName(style.justification) + "\\n";',
283
+ ' if (style.fillColor && style.fillColor.isValid) {',
284
+ ' info += " Color: " + style.fillColor.name + "\\n";',
285
+ ' }',
286
+ ' info += "\\n";',
287
+ ' }',
288
+ ' }',
289
+ ' }',
290
+ '',
291
+ ` if ("${escapedStyleType}" === "CHARACTER" || "${escapedStyleType}" === "ALL") {`,
292
+ ' info += "\\n=== CHARACTER STYLES ===\\n";',
293
+ ' for (var i = 0; i < doc.characterStyles.length; i++) {',
294
+ ' var style = doc.characterStyles[i];',
295
+ ' if (style.isValid) {',
296
+ ' info += "Name: " + style.name + "\\n";',
297
+ ' if (style.appliedFont && style.appliedFont.isValid) {',
298
+ ' info += " Font: " + style.appliedFont.name + "\\n";',
299
+ ' } else {',
300
+ ' info += " Font: [Not set]\\n";',
301
+ ' }',
302
+ ' info += " Size: " + style.pointSize + "pt\\n";',
303
+ ' info += " Style: " + style.fontStyle + "\\n";',
304
+ ' if (style.fillColor && style.fillColor.isValid) {',
305
+ ' info += " Color: " + style.fillColor.name + "\\n";',
306
+ ' }',
307
+ ' info += "\\n";',
308
+ ' }',
309
+ ' }',
310
+ ' }',
311
+ '',
312
+ ' info;',
313
+ '}'
314
+ ].join('\n');
315
+
316
+ const result = await ScriptExecutor.executeInDesignScript(script);
317
+ return formatResponse(result, "List Styles");
318
+ }
319
+
320
+ /**
321
+ * Create a color swatch
322
+ */
323
+ static async createColorSwatch(args) {
324
+ const {
325
+ name,
326
+ colorType = 'PROCESS',
327
+ red,
328
+ green,
329
+ blue
330
+ } = args;
331
+
332
+ const escapedName = escapeJsxString(name);
333
+
334
+ const script = [
335
+ 'if (app.documents.length === 0) {',
336
+ ' "No document open";',
337
+ '} else {',
338
+ ' var doc = app.activeDocument;',
339
+ '',
340
+ ' try {',
341
+ ` var color = doc.colors.itemByName("${escapedName}");`,
342
+ ' if (!color.isValid) {',
343
+ ` color = doc.colors.add({name: "${escapedName}"});`,
344
+ ' }',
345
+ '',
346
+ ' color.model = ColorModel.PROCESS;',
347
+ ' color.space = ColorSpace.RGB;',
348
+ ` color.colorValue = [${red}, ${green}, ${blue}];`,
349
+ '',
350
+ ' // Verify the color was set correctly',
351
+ ' var actualValues = color.colorValue;',
352
+ '',
353
+ ` "Color swatch '${escapedName}' created successfully with RGB values [" + actualValues[0] + ", " + actualValues[1] + ", " + actualValues[2] + "]";`,
354
+ ' } catch (error) {',
355
+ ' "Error creating color swatch: " + error.message;',
356
+ ' }',
357
+ '}'
358
+ ].join('\n');
359
+
360
+ const result = await ScriptExecutor.executeInDesignScript(script);
361
+ return formatResponse(result, "Create Color Swatch");
362
+ }
363
+
364
+ /**
365
+ * List all color swatches
366
+ */
367
+ static async listColorSwatches() {
368
+ const script = [
369
+ 'if (app.documents.length === 0) {',
370
+ ' "No document open";',
371
+ '} else {',
372
+ ' var doc = app.activeDocument;',
373
+ ' var info = "=== COLOR SWATCHES ===\\n";',
374
+ '',
375
+ ' try {',
376
+ ' for (var i = 0; i < doc.colors.length; i++) {',
377
+ ' var color = doc.colors[i];',
378
+ ' if (color && color.isValid) {',
379
+ ' info += "Name: " + color.name + "\\n";',
380
+ ' if (color.colorValue && color.colorValue.length >= 4) {',
381
+ ' info += " CMYK: [" + color.colorValue[0] + ", " + color.colorValue[1] + ", " + color.colorValue[2] + ", " + color.colorValue[3] + "]\\n";',
382
+ ' } else if (color.colorValue && color.colorValue.length >= 3) {',
383
+ ' info += " RGB: [" + color.colorValue[0] + ", " + color.colorValue[1] + ", " + color.colorValue[2] + "]\\n";',
384
+ ' } else {',
385
+ ' info += " Values: [No values set]\\n";',
386
+ ' }',
387
+ ' info += "\\n";',
388
+ ' }',
389
+ ' }',
390
+ ' } catch (error) {',
391
+ ' info += "Error listing colors: " + error.message + "\\n";',
392
+ ' }',
393
+ '',
394
+ ' info;',
395
+ '}'
396
+ ].join('\n');
397
+
398
+ const result = await ScriptExecutor.executeInDesignScript(script);
399
+ return formatResponse(result, "List Color Swatches");
400
+ }
401
+
402
+ /**
403
+ * Apply a color to text or graphics
404
+ */
405
+ static async applyColor(args) {
406
+ const { colorName, targetType = 'text', frameIndex = 0 } = args;
407
+ const escapedColorName = escapeJsxString(colorName);
408
+
409
+ const script = [
410
+ 'if (app.documents.length === 0) {',
411
+ ' "No document open";',
412
+ '} else {',
413
+ ' var doc = app.activeDocument;',
414
+ ' var page = doc.pages[0];',
415
+ '',
416
+ ' try {',
417
+ ` var color = doc.colors.itemByName("${escapedColorName}");`,
418
+ ' if (!color.isValid) {',
419
+ ` "Color '${escapedColorName}' not found";`,
420
+ ' } else {',
421
+ ` if ("${targetType}" === "text") {`,
422
+ ` if (${frameIndex} >= page.textFrames.length) {`,
423
+ ' "Text frame index out of range";',
424
+ ' } else {',
425
+ ` var textFrame = page.textFrames[${frameIndex}];`,
426
+ ' textFrame.texts[0].fillColor = color;',
427
+ ` "Color '${escapedColorName}' applied to text";`,
428
+ ' }',
429
+ ` } else if ("${targetType}" === "rectangle") {`,
430
+ ` if (${frameIndex} >= page.rectangles.length) {`,
431
+ ' "Rectangle index out of range";',
432
+ ' } else {',
433
+ ` var rectangle = page.rectangles[${frameIndex}];`,
434
+ ' rectangle.fillColor = color;',
435
+ ` "Color '${escapedColorName}' applied to rectangle";`,
436
+ ' }',
437
+ ` } else if ("${targetType}" === "ellipse") {`,
438
+ ` if (${frameIndex} >= page.ovals.length) {`,
439
+ ' "Ellipse index out of range";',
440
+ ' } else {',
441
+ ` var ellipse = page.ovals[${frameIndex}];`,
442
+ ' ellipse.fillColor = color;',
443
+ ` "Color '${escapedColorName}' applied to ellipse";`,
444
+ ' }',
445
+ ' } else {',
446
+ ' "Invalid target type. Use: text, rectangle, or ellipse";',
447
+ ' }',
448
+ ' }',
449
+ ' } catch (error) {',
450
+ ' "Error applying color: " + error.message;',
451
+ ' }',
452
+ '}'
453
+ ].join('\n');
454
+
455
+ const result = await ScriptExecutor.executeInDesignScript(script);
456
+ return formatResponse(result, "Apply Color");
457
+ }
458
+ }