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,220 @@
1
+ /**
2
+ * Presentation Handlers - Architecture presentation oriented utilities
3
+ */
4
+ import { ScriptExecutor } from '../core/scriptExecutor.js';
5
+ import { formatResponse } from '../utils/stringUtils.js';
6
+ import { escapeJsxString, escapeFilePathForJsx } from '../utils/stringUtils.js';
7
+ import { sessionManager } from '../core/sessionManager.js';
8
+
9
+ export class PresentationHandlers {
10
+ // 1) create_presentation_document: presets or custom size
11
+ static async createPresentationDocument(args) {
12
+ const { preset = 'A3_LANDSCAPE', width, height, pages = 1, facingPages = false } = args || {};
13
+
14
+ // default sizes in mm
15
+ const presets = {
16
+ A3_LANDSCAPE: { width: 420, height: 297 },
17
+ A4_LANDSCAPE: { width: 297, height: 210 },
18
+ RATIO_16x9: { width: 320, height: 180 },
19
+ };
20
+ const size = (width && height) ? { width, height } : (presets[preset] || presets.A3_LANDSCAPE);
21
+
22
+ const script = [
23
+ 'try {',
24
+ ' app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;',
25
+ ` var doc = app.documents.add({documentPreferences: {pageWidth: ${size.width}, pageHeight: ${size.height}, facingPages: ${!!facingPages}, pagesPerDocument: ${pages}}});`,
26
+ ' app.activeWindow.activePage = doc.pages[0];',
27
+ ' "Presentation document created: " + doc.name + ", size=" + doc.documentPreferences.pageWidth + "x" + doc.documentPreferences.pageHeight;',
28
+ '} catch (e) {',
29
+ ' "Error: " + e.message;',
30
+ '}'
31
+ ].join('\n');
32
+
33
+ const result = await ScriptExecutor.executeInDesignScript(script);
34
+ // update session
35
+ sessionManager.setPageDimensions({ width: size.width, height: size.height });
36
+ sessionManager.setActiveDocument({ name: 'Presentation', pageCount: pages });
37
+ return formatResponse(result, 'Create Presentation Document');
38
+ }
39
+
40
+ // 2) add_cover_page: title/subtitle/background image
41
+ static async addCoverPage(args) {
42
+ const { title = '项目汇报', subtitle = '', bgImagePath } = args || {};
43
+ const titleEsc = escapeJsxString(title);
44
+ const subtitleEsc = escapeJsxString(subtitle);
45
+ const bgPathEsc = bgImagePath ? escapeFilePathForJsx(bgImagePath) : null;
46
+
47
+ const scriptLines = [
48
+ 'try {',
49
+ ' app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;',
50
+ ' if (app.documents.length === 0) { "No document open"; }',
51
+ ' var doc = app.activeDocument;',
52
+ ' var page = doc.layoutWindows[0].activePage;',
53
+ ' var pageWidth = doc.documentPreferences.pageWidth;',
54
+ ' var pageHeight = doc.documentPreferences.pageHeight;',
55
+ ];
56
+
57
+ if (bgPathEsc) {
58
+ scriptLines.push(
59
+ ` var bgFile = File("${bgPathEsc}");`,
60
+ ' if (bgFile.exists) {',
61
+ ' var bgRect = page.rectangles.add({geometricBounds:[0,0,pageHeight,pageWidth]});',
62
+ ' bgRect.place(bgFile);',
63
+ ' bgRect.fit(FitOptions.FILL_PROPORTIONALLY);',
64
+ ' bgRect.sendToBack();',
65
+ ' }'
66
+ );
67
+ }
68
+
69
+ scriptLines.push(
70
+ ' var titleFrame = page.textFrames.add();',
71
+ ' titleFrame.geometricBounds = [pageHeight*0.35, pageWidth*0.08, pageHeight*0.55, pageWidth*0.92];',
72
+ ` titleFrame.contents = "${titleEsc}";`,
73
+ ' try { titleFrame.texts[0].pointSize = 48; } catch(e) {}',
74
+ ' try { titleFrame.paragraphs[0].justification = Justification.CENTER_ALIGN; } catch(e) {}',
75
+ ' var subFrame = null;'
76
+ );
77
+ if (subtitleEsc) {
78
+ scriptLines.push(
79
+ ' subFrame = page.textFrames.add();',
80
+ ' subFrame.geometricBounds = [pageHeight*0.60, pageWidth*0.2, pageHeight*0.68, pageWidth*0.8];',
81
+ ` subFrame.contents = "${subtitleEsc}";`,
82
+ ' try { subFrame.texts[0].pointSize = 18; } catch(e) {}',
83
+ ' try { subFrame.paragraphs[0].justification = Justification.CENTER_ALIGN; } catch(e) {}'
84
+ );
85
+ }
86
+
87
+ scriptLines.push(
88
+ ' "Cover page added";',
89
+ '} catch (e) { "Error: " + e.message; }'
90
+ );
91
+
92
+ const result = await ScriptExecutor.executeInDesignScript(scriptLines.join('\n'));
93
+ return formatResponse(result, 'Add Cover Page');
94
+ }
95
+
96
+ // 3) add_section_page: section title page
97
+ static async addSectionPage(args) {
98
+ const { title = '章节标题' } = args || {};
99
+ const titleEsc = escapeJsxString(title);
100
+
101
+ const script = [
102
+ 'try {',
103
+ ' app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;',
104
+ ' if (app.documents.length === 0) { "No document open"; }',
105
+ ' var doc = app.activeDocument;',
106
+ ' var page = doc.layoutWindows[0].activePage;',
107
+ ' var pageWidth = doc.documentPreferences.pageWidth;',
108
+ ' var pageHeight = doc.documentPreferences.pageHeight;',
109
+ ' var tf = page.textFrames.add();',
110
+ ' tf.geometricBounds = [pageHeight*0.40, pageWidth*0.1, pageHeight*0.60, pageWidth*0.9];',
111
+ ` tf.contents = "${titleEsc}";`,
112
+ ' try { tf.texts[0].pointSize = 36; } catch(e) {}',
113
+ ' try { tf.paragraphs[0].justification = Justification.CENTER_ALIGN; } catch(e) {}',
114
+ ' "Section page added";',
115
+ '} catch (e) { "Error: " + e.message; }'
116
+ ].join('\n');
117
+
118
+ const result = await ScriptExecutor.executeInDesignScript(script);
119
+ return formatResponse(result, 'Add Section Page');
120
+ }
121
+
122
+ // 4) add_full_bleed_image: one image filling page with optional caption
123
+ static async addFullBleedImage(args) {
124
+ const { filePath, caption } = args || {};
125
+ const fileEsc = escapeFilePathForJsx(filePath || '');
126
+ const captionEsc = caption ? escapeJsxString(caption) : null;
127
+
128
+ const script = [
129
+ 'try {',
130
+ ' app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;',
131
+ ' if (app.documents.length === 0) { "No document open"; }',
132
+ ' var doc = app.activeDocument;',
133
+ ' var page = doc.layoutWindows[0].activePage;',
134
+ ' var pageWidth = doc.documentPreferences.pageWidth;',
135
+ ' var pageHeight = doc.documentPreferences.pageHeight;',
136
+ ` var f = File("${fileEsc}");`,
137
+ ' if (!f.exists) { "Image not found"; }',
138
+ ' var rect = page.rectangles.add({geometricBounds:[0,0,pageHeight,pageWidth]});',
139
+ ' rect.place(f);',
140
+ ' rect.fit(FitOptions.FILL_PROPORTIONALLY);',
141
+ ' if ("' + (captionEsc || '') + '" !== "") {',
142
+ ' var cap = page.textFrames.add();',
143
+ ' cap.geometricBounds = [pageHeight*0.85, pageWidth*0.05, pageHeight*0.93, pageWidth*0.95];',
144
+ ` cap.contents = "${captionEsc || ''}";`,
145
+ ' try { cap.texts[0].pointSize = 11; } catch(e) {}',
146
+ ' try { cap.paragraphs[0].justification = Justification.LEFT_ALIGN; } catch(e) {}',
147
+ ' cap.fillColor = doc.swatches.itemByName("Paper");',
148
+ ' cap.transparencySettings.blendingSettings.opacity = 85;',
149
+ ' }',
150
+ ' "Full-bleed image added";',
151
+ '} catch (e) { "Error: " + e.message; }'
152
+ ].join('\n');
153
+
154
+ const result = await ScriptExecutor.executeInDesignScript(script);
155
+ return formatResponse(result, 'Add Full-Bleed Image');
156
+ }
157
+
158
+ // 5) add_image_grid: grid of images with rows/columns and gaps
159
+ static async addImageGrid(args) {
160
+ const { files = [], rows = 2, columns = 3, gap = 6 } = args || {};
161
+ const filesEsc = files.map(f => '"' + escapeFilePathForJsx(f) + '"').join(',');
162
+
163
+ const script = [
164
+ 'try {',
165
+ ' app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;',
166
+ ' if (app.documents.length === 0) { "No document open"; }',
167
+ ' var doc = app.activeDocument;',
168
+ ' var page = doc.layoutWindows[0].activePage;',
169
+ ' var W = doc.documentPreferences.pageWidth;',
170
+ ' var H = doc.documentPreferences.pageHeight;',
171
+ ` var files = [${filesEsc}];`,
172
+ ` var rows = ${Math.max(1, rows)};`,
173
+ ` var cols = ${Math.max(1, columns)};`,
174
+ ` var gap = ${Math.max(0, gap)};`,
175
+ ' var innerW = W - gap*(cols+1);',
176
+ ' var innerH = H - gap*(rows+1);',
177
+ ' var cellW = innerW/cols;',
178
+ ' var cellH = innerH/rows;',
179
+ ' var idx = 0;',
180
+ ' for (var r=0; r<rows; r++) {',
181
+ ' for (var c=0; c<cols; c++) {',
182
+ ' if (idx >= files.length) break;',
183
+ ' var x = gap + c*(cellW+gap);',
184
+ ' var y = gap + r*(cellH+gap);',
185
+ ' var rect = page.rectangles.add({geometricBounds:[y,x,y+cellH,x+cellW]});',
186
+ ' var f = File(files[idx]);',
187
+ ' if (f.exists) { rect.place(f); rect.fit(FitOptions.PROPORTIONALLY); rect.fit(FitOptions.CENTER_CONTENT); }',
188
+ ' idx++;',
189
+ ' }',
190
+ ' }',
191
+ ' "Image grid added";',
192
+ '} catch (e) { "Error: " + e.message; }'
193
+ ].join('\n');
194
+
195
+ const result = await ScriptExecutor.executeInDesignScript(script);
196
+ return formatResponse(result, 'Add Image Grid');
197
+ }
198
+
199
+ // 6) export_presentation_pdf: screen preset export
200
+ static async exportPresentationPDF(args) {
201
+ const { filePath = 'D:/Indesign-Exports/presentation.pdf', preset = 'High Quality Print' } = args || {};
202
+ const fileEsc = escapeFilePathForJsx(filePath);
203
+ const presetEsc = escapeJsxString(preset);
204
+
205
+ const script = [
206
+ 'try {',
207
+ ' app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;',
208
+ ' if (app.documents.length === 0) { "No document open"; }',
209
+ ' var doc = app.activeDocument;',
210
+ ` var pdfFile = File("${fileEsc}");`,
211
+ ' var folder = pdfFile.parent; if (folder && !folder.exists) { try { folder.create(); } catch(e) {} }',
212
+ ` doc.exportFile(ExportFormat.PDF_TYPE, pdfFile, false, "${presetEsc}");`,
213
+ ` "Presentation exported: ${fileEsc}";`,
214
+ '} catch (e) { "Error: " + e.message; }'
215
+ ].join('\n');
216
+
217
+ const result = await ScriptExecutor.executeInDesignScript(script);
218
+ return formatResponse(result, 'Export Presentation PDF');
219
+ }
220
+ }
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Spread management handlers
3
+ */
4
+ import { ScriptExecutor } from '../core/scriptExecutor.js';
5
+ import { formatResponse, escapeJsxString, escapeFilePathForJsx } from '../utils/stringUtils.js';
6
+
7
+ export class SpreadHandlers {
8
+ static async listSpreads() {
9
+ const script = [
10
+ 'if (app.documents.length === 0) {',
11
+ ' "No document open";',
12
+ '} else {',
13
+ ' var doc = app.activeDocument;',
14
+ ' var info = "=== SPREADS ===\\n";',
15
+ ' for (var i=0;i<doc.spreads.length;i++){',
16
+ ' var sp = doc.spreads[i];',
17
+ ' info += "Index: " + i + "\\n";',
18
+ ' info += "Pages: " + sp.pages.length + "\\n";',
19
+ ' info += "AllItems: " + sp.allPageItems.length + "\\n---\\n";',
20
+ ' }',
21
+ ' info;',
22
+ '}'
23
+ ].join('\n');
24
+ const result = await ScriptExecutor.executeInDesignScript(script);
25
+ return formatResponse(result, 'List Spreads');
26
+ }
27
+
28
+ static async getSpreadInfo(args) {
29
+ const { spreadIndex } = args;
30
+ const script = [
31
+ 'if (app.documents.length === 0) {',
32
+ ' "No document open";',
33
+ '} else {',
34
+ ' var doc = app.activeDocument;',
35
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
36
+ ' "Spread index out of range";',
37
+ ' } else {',
38
+ ` var sp = doc.spreads[${spreadIndex}];`,
39
+ ' var info = "=== SPREAD INFO ===\\n";',
40
+ ' info += "Index: " + sp.index + "\\n";',
41
+ ' info += "Pages: " + sp.pages.length + "\\n";',
42
+ ' info += "Allow Shuffle: " + sp.allowPageShuffle + "\\n";',
43
+ ' info += "Show Master Items: " + sp.showMasterItems + "\\n";',
44
+ ' try {',
45
+ ' info += "Hidden: " + (sp.visible === false ? "true" : "false") + "\\n";',
46
+ ' } catch (e) {',
47
+ ' info += "Hidden: Not available\\n";',
48
+ ' }',
49
+ ' info += "All Page Items: " + sp.allPageItems.length + "\\n";',
50
+ ' info;',
51
+ ' }',
52
+ '}'
53
+ ].join('\n');
54
+ const result = await ScriptExecutor.executeInDesignScript(script);
55
+ return formatResponse(result, 'Get Spread Info');
56
+ }
57
+
58
+ static async duplicateSpread(args) {
59
+ const { spreadIndex, position = 'AT_END', referenceSpreadIndex } = args;
60
+ const script = [
61
+ 'if (app.documents.length === 0) {',
62
+ ' "No document open";',
63
+ '} else {',
64
+ ' var doc = app.activeDocument;',
65
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
66
+ ' "Spread index out of range";',
67
+ ' } else {',
68
+ ` var sp = doc.spreads[${spreadIndex}];`,
69
+ ' var dup;',
70
+ ` if ("${position}" === "AT_BEGINNING") {`,
71
+ ' dup = sp.duplicate(LocationOptions.AT_BEGINNING);',
72
+ ` } else if ("${position}" === "BEFORE" && ${referenceSpreadIndex} !== undefined) {`,
73
+ ` dup = sp.duplicate(LocationOptions.BEFORE, doc.spreads[${referenceSpreadIndex}]);`,
74
+ ` } else if ("${position}" === "AFTER" && ${referenceSpreadIndex} !== undefined) {`,
75
+ ` dup = sp.duplicate(LocationOptions.AFTER, doc.spreads[${referenceSpreadIndex}]);`,
76
+ ' } else {',
77
+ ' dup = sp.duplicate();',
78
+ ' }',
79
+ ' "Spread duplicated. New index: " + dup.index;',
80
+ ' }',
81
+ '}'
82
+ ].join('\n');
83
+ const result = await ScriptExecutor.executeInDesignScript(script);
84
+ return formatResponse(result, 'Duplicate Spread');
85
+ }
86
+
87
+ static async moveSpread(args) {
88
+ const { spreadIndex, position = 'AT_END', referenceSpreadIndex } = args;
89
+ const script = [
90
+ 'if (app.documents.length === 0) {',
91
+ ' "No document open";',
92
+ '} else {',
93
+ ' var doc = app.activeDocument;',
94
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
95
+ ' "Spread index out of range";',
96
+ ' } else {',
97
+ ` var sp = doc.spreads[${spreadIndex}];`,
98
+ ` if ("${position}" === "AT_BEGINNING") {`,
99
+ ' sp.move(LocationOptions.AT_BEGINNING);',
100
+ ` } else if ("${position}" === "BEFORE" && ${referenceSpreadIndex} !== undefined) {`,
101
+ ` sp.move(LocationOptions.BEFORE, doc.spreads[${referenceSpreadIndex}]);`,
102
+ ` } else if ("${position}" === "AFTER" && ${referenceSpreadIndex} !== undefined) {`,
103
+ ` sp.move(LocationOptions.AFTER, doc.spreads[${referenceSpreadIndex}]);`,
104
+ ' } else {',
105
+ ' sp.move(LocationOptions.AT_END);',
106
+ ' }',
107
+ ' "Spread moved.";',
108
+ ' }',
109
+ '}'
110
+ ].join('\n');
111
+ const result = await ScriptExecutor.executeInDesignScript(script);
112
+ return formatResponse(result, 'Move Spread');
113
+ }
114
+
115
+ static async deleteSpread(args) {
116
+ const { spreadIndex } = args;
117
+ const script = [
118
+ 'if (app.documents.length === 0) {',
119
+ ' "No document open";',
120
+ '} else {',
121
+ ' var doc = app.activeDocument;',
122
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
123
+ ' "Spread index out of range";',
124
+ ' } else {',
125
+ ` doc.spreads[${spreadIndex}].remove();`,
126
+ ' "Spread deleted";',
127
+ ' }',
128
+ '}'
129
+ ].join('\n');
130
+ const result = await ScriptExecutor.executeInDesignScript(script);
131
+ return formatResponse(result, 'Delete Spread');
132
+ }
133
+
134
+ static async setSpreadProperties(args) {
135
+ const { spreadIndex, name, allowPageShuffle, showMasterItems, spreadHidden } = args;
136
+ const escapedName = name ? escapeJsxString(name) : '';
137
+ const script = [
138
+ 'if (app.documents.length === 0) {',
139
+ ' "No document open";',
140
+ '} else {',
141
+ ' var doc = app.activeDocument;',
142
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
143
+ ' "Spread index out of range";',
144
+ ' } else {',
145
+ ` var sp = doc.spreads[${spreadIndex}];`,
146
+ ` if ("${escapedName}" !== "") try { sp.label = "${escapedName}"; } catch(e) {}`,
147
+ ` if (${allowPageShuffle} !== undefined) sp.allowPageShuffle = ${!!allowPageShuffle};`,
148
+ ` if (${showMasterItems} !== undefined) sp.showMasterItems = ${!!showMasterItems};`,
149
+ ` if (${spreadHidden} !== undefined) try { sp.visible = ${!spreadHidden}; } catch(e) {}`,
150
+ ' "Spread properties updated";',
151
+ ' }',
152
+ '}'
153
+ ].join('\n');
154
+ const result = await ScriptExecutor.executeInDesignScript(script);
155
+ return formatResponse(result, 'Set Spread Properties');
156
+ }
157
+
158
+ static async createSpreadGuides(args) {
159
+ const { spreadIndex, numberOfRows = 0, numberOfColumns = 0, rowGutter, columnGutter, guideColor = 'BLUE', fitMargins = true, removeExisting = false, layerName } = args;
160
+ const escapedLayerName = layerName ? escapeJsxString(layerName) : '';
161
+ const normalizedGuideColor = typeof guideColor === 'string' ? guideColor.trim() : 'BLUE';
162
+ const guideColorLiteral = /^\s*\[/.test(normalizedGuideColor)
163
+ ? normalizedGuideColor
164
+ : `UIColors.${/^[A-Z_]+$/.test(normalizedGuideColor.toUpperCase()) ? normalizedGuideColor.toUpperCase() : 'BLUE'}`;
165
+ const script = [
166
+ 'if (app.documents.length === 0) {',
167
+ ' "No document open";',
168
+ '} else {',
169
+ ' var doc = app.activeDocument;',
170
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
171
+ ' "Spread index out of range";',
172
+ ' } else {',
173
+ ` var sp = doc.spreads[${spreadIndex}];`,
174
+ (escapedLayerName ? ` var layer = doc.layers.itemByName("${escapedLayerName}");
175
+ try { if (layer.isValid) sp = layer; } catch(e) {}
176
+ ` : ''),
177
+ ` sp.createGuides(${numberOfRows}, ${numberOfColumns}, "${rowGutter || ''}", "${columnGutter || ''}", ${guideColorLiteral}, ${fitMargins}, ${removeExisting});`,
178
+ ' "Spread guides created";',
179
+ ' }',
180
+ '}'
181
+ ].join('\n');
182
+ const result = await ScriptExecutor.executeInDesignScript(script);
183
+ return formatResponse(result, 'Create Spread Guides');
184
+ }
185
+
186
+ static async placeFileOnSpread(args) {
187
+ const {
188
+ spreadIndex,
189
+ filePath,
190
+ x = 10,
191
+ y = 10,
192
+ layerName,
193
+ showingOptions = false,
194
+ autoflowing = false,
195
+ pageIndexWithinSpread = 0
196
+ } = args;
197
+ const fileEsc = escapeFilePathForJsx(filePath);
198
+ const layerEsc = layerName ? escapeJsxString(layerName) : '';
199
+ const pageIndexLiteral = Number.isInteger(pageIndexWithinSpread) ? pageIndexWithinSpread : 0;
200
+
201
+ const scriptLines = [
202
+ 'if (app.documents.length === 0) {',
203
+ ' "No document open";',
204
+ '} else {',
205
+ ' var doc = app.activeDocument;',
206
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
207
+ ' "Spread index out of range";',
208
+ ' } else {',
209
+ ` var sp = doc.spreads[${spreadIndex}];`,
210
+ ` var targetPageIndex = ${pageIndexLiteral};`,
211
+ ' var page = null;',
212
+ ' if (targetPageIndex >= 0 && targetPageIndex < sp.pages.length) {',
213
+ ' page = sp.pages[targetPageIndex];',
214
+ ' } else if (sp.pages.length > 0) {',
215
+ ' page = sp.pages[0];',
216
+ ' }',
217
+ ' if (!page) {',
218
+ ' "Spread contains no pages";',
219
+ ' } else {',
220
+ ` var file = File("${fileEsc}");`,
221
+ ' try {',
222
+ ' var placedItem;',
223
+ ' var layerToUse = null;',
224
+ ` var layerNameEscaped = "${layerEsc}";`,
225
+ ' if (layerNameEscaped !== "") {',
226
+ ' try {',
227
+ ' layerToUse = doc.layers.itemByName(layerNameEscaped);',
228
+ ' if (!layerToUse || !layerToUse.isValid) {',
229
+ ' layerToUse = null;',
230
+ ' }',
231
+ ' } catch (layerError) {',
232
+ ' layerToUse = null;',
233
+ ' }',
234
+ ' }',
235
+ ' var previousLayer = doc.activeLayer;',
236
+ ' if (layerToUse) { doc.activeLayer = layerToUse; }',
237
+ ` placedItem = page.place(file, [${x}, ${y}]);`,
238
+ ' try { doc.activeLayer = previousLayer; } catch (restoreLayerError) {}',
239
+ ' "File placed on spread";',
240
+ ' } catch (e) {',
241
+ ' try { if (previousLayer && previousLayer.isValid) doc.activeLayer = previousLayer; } catch (restoreLayerError) {}',
242
+ ' "Error placing file: " + e.message;',
243
+ ' }',
244
+ ' }',
245
+ ' }',
246
+ '}'
247
+ ];
248
+
249
+ const script = scriptLines.join('\n');
250
+
251
+ const result = await ScriptExecutor.executeInDesignScript(script);
252
+ return formatResponse(result, 'Place File on Spread');
253
+ }
254
+
255
+ static async placeXmlOnSpread(args) {
256
+ const { spreadIndex, xmlElementName, x = 10, y = 10, autoflowing = false, pageIndexWithinSpread = 0 } = args;
257
+ const xmlEsc = escapeJsxString(xmlElementName);
258
+ const pageIndexLiteral = Number.isInteger(pageIndexWithinSpread) ? pageIndexWithinSpread : 0;
259
+
260
+ const script = [
261
+ 'if (app.documents.length === 0) {',
262
+ ' "No document open";',
263
+ '} else {',
264
+ ' var doc = app.activeDocument;',
265
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
266
+ ' "Spread index out of range";',
267
+ ' } else {',
268
+ ` var sp = doc.spreads[${spreadIndex}];`,
269
+ ` var targetPageIndex = ${pageIndexLiteral};`,
270
+ ' var page = null;',
271
+ ' if (targetPageIndex >= 0 && targetPageIndex < sp.pages.length) {',
272
+ ' page = sp.pages[targetPageIndex];',
273
+ ' } else if (sp.pages.length > 0) {',
274
+ ' page = sp.pages[0];',
275
+ ' }',
276
+ ' if (!page) {',
277
+ ' "Spread contains no pages";',
278
+ ' } else {',
279
+ ` var elements = doc.xmlElements.itemByName("${xmlEsc}");`,
280
+ ' try {',
281
+ ` page.placeXML(elements, [${x}, ${y}], ${autoflowing});`,
282
+ ' "XML placed on spread";',
283
+ ' } catch (e) {',
284
+ ' "Error placing XML: " + e.message;',
285
+ ' }',
286
+ ' }',
287
+ ' }',
288
+ '}'
289
+ ].join('\n');
290
+ const result = await ScriptExecutor.executeInDesignScript(script);
291
+ return formatResponse(result, 'Place XML on Spread');
292
+ }
293
+ static async selectSpread(args) {
294
+ const { spreadIndex, selectionMode = 'REPLACE_WITH' } = args;
295
+ const script = [
296
+ 'if (app.documents.length === 0) {',
297
+ ' "No document open";',
298
+ '} else {',
299
+ ' var doc = app.activeDocument;',
300
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
301
+ ' "Spread index out of range";',
302
+ ' } else {',
303
+ ` var sp = doc.spreads[${spreadIndex}];`,
304
+ ' try {',
305
+ ` var mode = SelectionOptions.REPLACE_WITH;`,
306
+ ` if ("${selectionMode}" === "ADD_TO") mode = SelectionOptions.ADD_TO;`,
307
+ ` else if ("${selectionMode}" === "REMOVE_FROM") mode = SelectionOptions.REMOVE_FROM;`,
308
+ ` else if ("${selectionMode}" === "SET_KEY") mode = SelectionOptions.SET_KEY;`,
309
+ ' app.select(sp, mode);',
310
+ ' "Spread selected";',
311
+ ' } catch (e) {',
312
+ ' "Error selecting spread: " + e.message;',
313
+ ' }',
314
+ ' }',
315
+ '}'
316
+ ].join('\n');
317
+ const result = await ScriptExecutor.executeInDesignScript(script);
318
+ return formatResponse(result, 'Select Spread');
319
+ }
320
+
321
+ static async getSpreadContentSummary(args) {
322
+ const { spreadIndex } = args;
323
+ const script = [
324
+ 'if (app.documents.length === 0) {',
325
+ ' "No document open";',
326
+ '} else {',
327
+ ' var doc = app.activeDocument;',
328
+ ` if (${spreadIndex} >= doc.spreads.length) {`,
329
+ ' "Spread index out of range";',
330
+ ' } else {',
331
+ ` var sp = doc.spreads[${spreadIndex}];`,
332
+ ' var info = "=== SPREAD CONTENT SUMMARY ===\\n";',
333
+ ' info += "Text Frames: " + sp.textFrames.length + "\\n";',
334
+ ' info += "Rectangles: " + sp.rectangles.length + "\\n";',
335
+ ' info += "Ovals: " + sp.ovals.length + "\\n";',
336
+ ' info += "Polygons: " + sp.polygons.length + "\\n";',
337
+ ' info += "Groups: " + sp.groups.length + "\\n";',
338
+ ' info += "Guides: " + sp.guides.length + "\\n";',
339
+ ' info += "All Page Items: " + sp.allPageItems.length + "\\n";',
340
+ ' info;',
341
+ ' }',
342
+ '}'
343
+ ].join('\n');
344
+ const result = await ScriptExecutor.executeInDesignScript(script);
345
+ return formatResponse(result, 'Get Spread Content Summary');
346
+ }
347
+ }
348
+