@hybridlabor-api/aos 4.0.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.claude/agents/database-reviewer.md +109 -0
  2. package/.claude/agents/go-build-resolver.md +112 -0
  3. package/.claude/agents/opensource-forker.md +216 -0
  4. package/.claude/agents/opensource-sanitizer.md +206 -0
  5. package/.claude/agents/security-reviewer.md +126 -0
  6. package/.claude/agents/silent-failure-hunter.md +68 -0
  7. package/CLAUDE.md +15 -0
  8. package/README.md +0 -13
  9. package/THIRD_PARTY_NOTICES.md +133 -0
  10. package/package.json +4 -2
  11. package/skills/basic/bdbmediastorm/SKILL.md +7 -1
  12. package/skills/basic/startcycle/SKILL.md +2 -0
  13. package/skills/bdbrainstorm/SKILL.md +1 -0
  14. package/skills/global_config/plan-canvas/SKILL.md +233 -0
  15. package/skills/global_config/plan-canvas/scripts/lib/loopback-guard.js +59 -0
  16. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/markdown.js +301 -0
  17. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sdk.js +239 -0
  18. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +636 -0
  19. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/sessions.js +271 -0
  20. package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +630 -0
  21. package/skills/global_config/plan-canvas/scripts/plan-canvas.js +419 -0
  22. package/.agents/skills/firecrawl/SKILL.md +0 -149
  23. package/.agents/skills/firecrawl/rules/install.md +0 -82
  24. package/.agents/skills/firecrawl/rules/security.md +0 -26
  25. package/.agents/skills/firecrawl-agent/SKILL.md +0 -58
  26. package/.agents/skills/firecrawl-build/SKILL.md +0 -39
  27. package/.agents/skills/firecrawl-build-interact/SKILL.md +0 -68
  28. package/.agents/skills/firecrawl-build-onboarding/SKILL.md +0 -103
  29. package/.agents/skills/firecrawl-build-onboarding/references/auth-flow.md +0 -39
  30. package/.agents/skills/firecrawl-build-onboarding/references/project-setup.md +0 -20
  31. package/.agents/skills/firecrawl-build-onboarding/references/sdk-installation.md +0 -17
  32. package/.agents/skills/firecrawl-build-scrape/SKILL.md +0 -69
  33. package/.agents/skills/firecrawl-build-search/SKILL.md +0 -69
  34. package/.agents/skills/firecrawl-crawl/SKILL.md +0 -59
  35. package/.agents/skills/firecrawl-download/SKILL.md +0 -70
  36. package/.agents/skills/firecrawl-interact/SKILL.md +0 -84
  37. package/.agents/skills/firecrawl-map/SKILL.md +0 -51
  38. package/.agents/skills/firecrawl-scrape/SKILL.md +0 -69
  39. package/.agents/skills/firecrawl-search/SKILL.md +0 -60
  40. package/docs/sessions/AUDIT-HANDOVER-2026-08-28.md +0 -169
  41. package/docs/sessions/BDB_REMOTEOS_MCP_HANDOVER.md +0 -130
  42. package/docs/sessions/SESSION-HANDOVER-v3.13.md +0 -249
  43. package/mcps/RhinoMCP/cc-plugin/.claude/settings.json +0 -10
  44. package/mcps/after-effects-mcp/build/index.js +0 -840
  45. package/mcps/after-effects-mcp/build/scripts/applyEffect.jsx +0 -153
  46. package/mcps/after-effects-mcp/build/scripts/applyEffectTemplate.jsx +0 -218
  47. package/mcps/after-effects-mcp/build/scripts/createComposition.jsx +0 -71
  48. package/mcps/after-effects-mcp/build/scripts/createShapeLayer.jsx +0 -147
  49. package/mcps/after-effects-mcp/build/scripts/createSolidLayer.jsx +0 -114
  50. package/mcps/after-effects-mcp/build/scripts/createTextLayer.jsx +0 -115
  51. package/mcps/after-effects-mcp/build/scripts/getLayerInfo.jsx +0 -192
  52. package/mcps/after-effects-mcp/build/scripts/getProjectInfo.jsx +0 -90
  53. package/mcps/after-effects-mcp/build/scripts/listCompositions.jsx +0 -50
  54. package/mcps/after-effects-mcp/build/scripts/mcp-bridge-auto.jsx +0 -1773
  55. package/mcps/after-effects-mcp/build/scripts/setLayerProperties.jsx +0 -160
  56. package/mcps/bdb-remoteos-mcp/queue.db +0 -0
  57. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
  58. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/incus_client.cpython-312.pyc +0 -0
  59. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/main.cpython-312.pyc +0 -0
  60. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/queue.cpython-312.pyc +0 -0
  61. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/schemas.cpython-312.pyc +0 -0
  62. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/server.cpython-312.pyc +0 -0
  63. package/mcps/bdb-remoteos-mcp/src/bdb_remoteos_mcp/__pycache__/webhook.cpython-312.pyc +0 -0
  64. package/mcps/bdb-remoteos-mcp/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  65. package/mcps/bdb-remoteos-mcp/tests/__pycache__/mock_incus.cpython-312.pyc +0 -0
  66. package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_mcp_server.cpython-312-pytest-9.1.1.pyc +0 -0
  67. package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_security_redteam.cpython-312-pytest-9.1.1.pyc +0 -0
  68. package/mcps/bdb-remoteos-mcp/tests/__pycache__/test_webhook.cpython-312-pytest-9.1.1.pyc +0 -0
  69. package/mcps/computer-use-mcp/dist/client.d.ts +0 -150
  70. package/mcps/computer-use-mcp/dist/client.js +0 -136
  71. package/mcps/computer-use-mcp/dist/entrypoint.d.ts +0 -16
  72. package/mcps/computer-use-mcp/dist/entrypoint.js +0 -26
  73. package/mcps/computer-use-mcp/dist/native.d.ts +0 -212
  74. package/mcps/computer-use-mcp/dist/native.js +0 -50
  75. package/mcps/computer-use-mcp/dist/server.d.ts +0 -32
  76. package/mcps/computer-use-mcp/dist/server.js +0 -342
  77. package/mcps/computer-use-mcp/dist/session.d.ts +0 -101
  78. package/mcps/computer-use-mcp/dist/session.js +0 -2372
  79. package/skills/bdbsaastraining/scripts/__pycache__/build_profile.cpython-314.pyc +0 -0
@@ -1,1773 +0,0 @@
1
- // mcp-bridge-auto.jsx
2
- // Auto-running MCP Bridge panel for After Effects
3
-
4
- // Remove #include directives as we define functions below
5
- /*
6
- #include "createComposition.jsx"
7
- #include "createTextLayer.jsx"
8
- #include "createShapeLayer.jsx"
9
- #include "createSolidLayer.jsx"
10
- #include "setLayerProperties.jsx"
11
- */
12
-
13
- // --- Function Definitions ---
14
-
15
- // --- createComposition (from createComposition.jsx) ---
16
- function createComposition(args) {
17
- try {
18
- var name = args.name || "New Composition";
19
- var width = parseInt(args.width) || 1920;
20
- var height = parseInt(args.height) || 1080;
21
- var pixelAspect = parseFloat(args.pixelAspect) || 1.0;
22
- var duration = parseFloat(args.duration) || 10.0;
23
- var frameRate = parseFloat(args.frameRate) || 30.0;
24
- var bgColor = args.backgroundColor ? [args.backgroundColor.r/255, args.backgroundColor.g/255, args.backgroundColor.b/255] : [0, 0, 0];
25
- var newComp = app.project.items.addComp(name, width, height, pixelAspect, duration, frameRate);
26
- if (args.backgroundColor) {
27
- newComp.bgColor = bgColor;
28
- }
29
- return JSON.stringify({
30
- status: "success", message: "Composition created successfully",
31
- composition: { name: newComp.name, id: newComp.id, width: newComp.width, height: newComp.height, pixelAspect: newComp.pixelAspect, duration: newComp.duration, frameRate: newComp.frameRate, bgColor: newComp.bgColor }
32
- }, null, 2);
33
- } catch (error) {
34
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
35
- }
36
- }
37
-
38
- // --- createTextLayer (from createTextLayer.jsx) ---
39
- function createTextLayer(args) {
40
- try {
41
- var compName = args.compName || "";
42
- var text = args.text || "Text Layer";
43
- var position = args.position || [960, 540];
44
- var fontSize = args.fontSize || 72;
45
- var color = args.color || [1, 1, 1];
46
- var startTime = args.startTime || 0;
47
- var duration = args.duration || 5;
48
- var fontFamily = args.fontFamily || "Arial";
49
- var alignment = args.alignment || "center";
50
- var comp = null;
51
- for (var i = 1; i <= app.project.numItems; i++) {
52
- var item = app.project.item(i);
53
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
54
- }
55
- if (!comp) {
56
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
57
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
58
- }
59
- var textLayer = comp.layers.addText(text);
60
- var textProp = textLayer.property("ADBE Text Properties").property("ADBE Text Document");
61
- var textDocument = textProp.value;
62
- textDocument.fontSize = fontSize;
63
- textDocument.fillColor = color;
64
- textDocument.font = fontFamily;
65
- if (alignment === "left") { textDocument.justification = ParagraphJustification.LEFT_JUSTIFY; }
66
- else if (alignment === "center") { textDocument.justification = ParagraphJustification.CENTER_JUSTIFY; }
67
- else if (alignment === "right") { textDocument.justification = ParagraphJustification.RIGHT_JUSTIFY; }
68
- textProp.setValue(textDocument);
69
- textLayer.property("Position").setValue(position);
70
- textLayer.startTime = startTime;
71
- if (duration > 0) { textLayer.outPoint = startTime + duration; }
72
- return JSON.stringify({
73
- status: "success", message: "Text layer created successfully",
74
- layer: { name: textLayer.name, index: textLayer.index, type: "text", inPoint: textLayer.inPoint, outPoint: textLayer.outPoint, position: textLayer.property("Position").value }
75
- }, null, 2);
76
- } catch (error) {
77
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
78
- }
79
- }
80
-
81
- // --- createShapeLayer (from createShapeLayer.jsx) ---
82
- function createShapeLayer(args) {
83
- try {
84
- var compName = args.compName || "";
85
- var shapeType = args.shapeType || "rectangle";
86
- var position = args.position || [960, 540];
87
- var size = args.size || [200, 200];
88
- var fillColor = args.fillColor || [1, 0, 0];
89
- var strokeColor = args.strokeColor || [0, 0, 0];
90
- var strokeWidth = args.strokeWidth || 0;
91
- var startTime = args.startTime || 0;
92
- var duration = args.duration || 5;
93
- var name = args.name || "Shape Layer";
94
- var points = args.points || 5;
95
- var comp = null;
96
- for (var i = 1; i <= app.project.numItems; i++) {
97
- var item = app.project.item(i);
98
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
99
- }
100
- if (!comp) {
101
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
102
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
103
- }
104
- var shapeLayer = comp.layers.addShape();
105
- shapeLayer.name = name;
106
- var contents = shapeLayer.property("Contents");
107
- var shapeGroup = contents.addProperty("ADBE Vector Group");
108
- var groupContents = shapeGroup.property("Contents");
109
- var shapePathProperty;
110
- if (shapeType === "rectangle") {
111
- shapePathProperty = groupContents.addProperty("ADBE Vector Shape - Rect");
112
- shapePathProperty.property("Size").setValue(size);
113
- } else if (shapeType === "ellipse") {
114
- shapePathProperty = groupContents.addProperty("ADBE Vector Shape - Ellipse");
115
- shapePathProperty.property("Size").setValue(size);
116
- } else if (shapeType === "polygon" || shapeType === "star") {
117
- shapePathProperty = groupContents.addProperty("ADBE Vector Shape - Star");
118
- shapePathProperty.property("Type").setValue(shapeType === "polygon" ? 1 : 2);
119
- shapePathProperty.property("Points").setValue(points);
120
- shapePathProperty.property("Outer Radius").setValue(size[0] / 2);
121
- if (shapeType === "star") { shapePathProperty.property("Inner Radius").setValue(size[0] / 3); }
122
- }
123
- var fill = groupContents.addProperty("ADBE Vector Graphic - Fill");
124
- fill.property("Color").setValue(fillColor);
125
- fill.property("Opacity").setValue(100);
126
- if (strokeWidth > 0) {
127
- var stroke = groupContents.addProperty("ADBE Vector Graphic - Stroke");
128
- stroke.property("Color").setValue(strokeColor);
129
- stroke.property("Stroke Width").setValue(strokeWidth);
130
- stroke.property("Opacity").setValue(100);
131
- }
132
- shapeLayer.property("Position").setValue(position);
133
- shapeLayer.startTime = startTime;
134
- if (duration > 0) { shapeLayer.outPoint = startTime + duration; }
135
- return JSON.stringify({
136
- status: "success", message: "Shape layer created successfully",
137
- layer: { name: shapeLayer.name, index: shapeLayer.index, type: "shape", shapeType: shapeType, inPoint: shapeLayer.inPoint, outPoint: shapeLayer.outPoint, position: shapeLayer.property("Position").value }
138
- }, null, 2);
139
- } catch (error) {
140
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
141
- }
142
- }
143
-
144
- // --- createCamera ---
145
- function createCamera(args) {
146
- try {
147
- var compName = args.compName || "";
148
- var name = args.name || "Camera";
149
- var zoom = args.zoom || 1777.78; // Default ~50mm equivalent
150
- var position = args.position; // Optional [x, y, z]
151
- var pointOfInterest = args.pointOfInterest; // Optional [x, y, z]
152
- var oneNode = args.oneNode || false; // If true, create a one-node camera (no point of interest)
153
-
154
- var comp = null;
155
- for (var i = 1; i <= app.project.numItems; i++) {
156
- var item = app.project.item(i);
157
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
158
- }
159
- if (!comp) {
160
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
161
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
162
- }
163
-
164
- var centerPoint = [comp.width / 2, comp.height / 2];
165
- var cameraLayer = comp.layers.addCamera(name, centerPoint);
166
- cameraLayer.property("Camera Options").property("Zoom").setValue(zoom);
167
-
168
- if (oneNode) {
169
- cameraLayer.autoOrient = AutoOrientType.NO_AUTO_ORIENT;
170
- }
171
-
172
- if (position !== undefined && position !== null) {
173
- cameraLayer.property("Position").setValue(position);
174
- }
175
-
176
- if (pointOfInterest !== undefined && pointOfInterest !== null && !oneNode) {
177
- cameraLayer.property("Point of Interest").setValue(pointOfInterest);
178
- }
179
-
180
- var result = {
181
- name: cameraLayer.name,
182
- index: cameraLayer.index,
183
- zoom: cameraLayer.property("Camera Options").property("Zoom").value,
184
- position: cameraLayer.property("Position").value,
185
- oneNode: oneNode
186
- };
187
- if (!oneNode) {
188
- result.pointOfInterest = cameraLayer.property("Point of Interest").value;
189
- }
190
-
191
- return JSON.stringify({
192
- status: "success",
193
- message: "Camera created successfully",
194
- layer: result
195
- }, null, 2);
196
- } catch (error) {
197
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
198
- }
199
- }
200
-
201
- // --- duplicateLayer ---
202
- function duplicateLayer(args) {
203
- try {
204
- var compName = args.compName || "";
205
- var layerIndex = args.layerIndex;
206
- var layerName = args.layerName || "";
207
- var newName = args.newName; // optional rename
208
-
209
- var comp = null;
210
- for (var i = 1; i <= app.project.numItems; i++) {
211
- var item = app.project.item(i);
212
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
213
- }
214
- if (!comp) {
215
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
216
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
217
- }
218
-
219
- var layer = null;
220
- if (layerIndex !== undefined && layerIndex !== null) {
221
- if (layerIndex > 0 && layerIndex <= comp.numLayers) { layer = comp.layer(layerIndex); }
222
- else { throw new Error("Layer index out of bounds: " + layerIndex); }
223
- } else if (layerName) {
224
- for (var j = 1; j <= comp.numLayers; j++) {
225
- if (comp.layer(j).name === layerName) { layer = comp.layer(j); break; }
226
- }
227
- }
228
- if (!layer) { throw new Error("Layer not found: " + (layerName || "index " + layerIndex)); }
229
-
230
- var newLayer = layer.duplicate();
231
- if (newName) { newLayer.name = newName; }
232
-
233
- return JSON.stringify({
234
- status: "success",
235
- message: "Layer duplicated successfully",
236
- original: { name: layer.name, index: layer.index },
237
- duplicate: { name: newLayer.name, index: newLayer.index }
238
- }, null, 2);
239
- } catch (error) {
240
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
241
- }
242
- }
243
-
244
- // --- deleteLayer ---
245
- function deleteLayer(args) {
246
- try {
247
- var compName = args.compName || "";
248
- var layerIndex = args.layerIndex;
249
- var layerName = args.layerName || "";
250
-
251
- var comp = null;
252
- for (var i = 1; i <= app.project.numItems; i++) {
253
- var item = app.project.item(i);
254
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
255
- }
256
- if (!comp) {
257
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
258
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
259
- }
260
-
261
- var layer = null;
262
- if (layerIndex !== undefined && layerIndex !== null) {
263
- if (layerIndex > 0 && layerIndex <= comp.numLayers) { layer = comp.layer(layerIndex); }
264
- else { throw new Error("Layer index out of bounds: " + layerIndex); }
265
- } else if (layerName) {
266
- for (var j = 1; j <= comp.numLayers; j++) {
267
- if (comp.layer(j).name === layerName) { layer = comp.layer(j); break; }
268
- }
269
- }
270
- if (!layer) { throw new Error("Layer not found: " + (layerName || "index " + layerIndex)); }
271
-
272
- var deletedName = layer.name;
273
- var deletedIndex = layer.index;
274
- layer.remove();
275
-
276
- return JSON.stringify({
277
- status: "success",
278
- message: "Layer deleted successfully",
279
- deleted: { name: deletedName, index: deletedIndex }
280
- }, null, 2);
281
- } catch (error) {
282
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
283
- }
284
- }
285
-
286
- // --- setLayerMask: create or modify a mask on a layer ---
287
- function setLayerMask(args) {
288
- try {
289
- var compName = args.compName || "";
290
- var layerIndex = args.layerIndex;
291
- var layerName = args.layerName || "";
292
- var maskIndex = args.maskIndex; // optional — if provided, modify existing mask
293
- var maskPath = args.maskPath; // array of [x, y] points defining the mask shape
294
- var maskRect = args.maskRect; // shorthand: {top, left, width, height} for rectangular masks
295
- var maskMode = args.maskMode || "add"; // "add", "subtract", "intersect", "none"
296
- var maskFeather = args.maskFeather; // optional [x, y] feather
297
- var maskOpacity = args.maskOpacity; // optional 0-100
298
- var maskExpansion = args.maskExpansion; // optional pixels
299
- var maskName = args.maskName; // optional rename
300
-
301
- var comp = null;
302
- for (var i = 1; i <= app.project.numItems; i++) {
303
- var item = app.project.item(i);
304
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
305
- }
306
- if (!comp) {
307
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
308
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
309
- }
310
-
311
- var layer = null;
312
- if (layerIndex !== undefined && layerIndex !== null) {
313
- if (layerIndex > 0 && layerIndex <= comp.numLayers) { layer = comp.layer(layerIndex); }
314
- else { throw new Error("Layer index out of bounds: " + layerIndex); }
315
- } else if (layerName) {
316
- for (var j = 1; j <= comp.numLayers; j++) {
317
- if (comp.layer(j).name === layerName) { layer = comp.layer(j); break; }
318
- }
319
- }
320
- if (!layer) { throw new Error("Layer not found: " + (layerName || "index " + layerIndex)); }
321
-
322
- // Build the mask shape
323
- var shapePoints = [];
324
- if (maskRect) {
325
- // Rectangle shorthand
326
- var t = maskRect.top || 0;
327
- var l = maskRect.left || 0;
328
- var w = maskRect.width || comp.width;
329
- var h = maskRect.height || comp.height;
330
- shapePoints = [[l, t], [l + w, t], [l + w, t + h], [l, t + h]];
331
- } else if (maskPath && maskPath.length >= 3) {
332
- shapePoints = maskPath;
333
- } else {
334
- throw new Error("Must provide either maskRect or maskPath with at least 3 points");
335
- }
336
-
337
- // Create the shape object
338
- var myShape = new Shape();
339
- var vertices = [];
340
- for (var p = 0; p < shapePoints.length; p++) {
341
- vertices.push(shapePoints[p]);
342
- }
343
- myShape.vertices = vertices;
344
- myShape.closed = true;
345
-
346
- var changed = [];
347
- var mask;
348
-
349
- if (maskIndex !== undefined && maskIndex !== null) {
350
- // Modify existing mask
351
- if (maskIndex > 0 && maskIndex <= layer.property("Masks").numProperties) {
352
- mask = layer.property("Masks").property(maskIndex);
353
- } else {
354
- throw new Error("Mask index out of bounds: " + maskIndex);
355
- }
356
- mask.property("Mask Path").setValue(myShape);
357
- changed.push("maskPath");
358
- } else {
359
- // Create new mask
360
- mask = layer.property("Masks").addProperty("Mask");
361
- mask.property("Mask Path").setValue(myShape);
362
- changed.push("newMask");
363
- }
364
-
365
- // Set mask mode
366
- var modes = {
367
- "none": MaskMode.NONE,
368
- "add": MaskMode.ADD,
369
- "subtract": MaskMode.SUBTRACT,
370
- "intersect": MaskMode.INTERSECT,
371
- "lighten": MaskMode.LIGHTEN,
372
- "darken": MaskMode.DARKEN,
373
- "difference": MaskMode.DIFFERENCE
374
- };
375
- if (modes[maskMode] !== undefined) {
376
- mask.maskMode = modes[maskMode];
377
- changed.push("maskMode");
378
- }
379
-
380
- if (maskFeather !== undefined && maskFeather !== null) {
381
- mask.property("Mask Feather").setValue(maskFeather);
382
- changed.push("maskFeather");
383
- }
384
- if (maskOpacity !== undefined && maskOpacity !== null) {
385
- mask.property("Mask Opacity").setValue(maskOpacity);
386
- changed.push("maskOpacity");
387
- }
388
- if (maskExpansion !== undefined && maskExpansion !== null) {
389
- mask.property("Mask Expansion").setValue(maskExpansion);
390
- changed.push("maskExpansion");
391
- }
392
- if (maskName) {
393
- mask.name = maskName;
394
- changed.push("maskName");
395
- }
396
-
397
- return JSON.stringify({
398
- status: "success",
399
- message: "Mask set successfully",
400
- layer: { name: layer.name, index: layer.index },
401
- mask: {
402
- name: mask.name,
403
- index: mask.propertyIndex,
404
- mode: maskMode,
405
- changedProperties: changed
406
- }
407
- }, null, 2);
408
- } catch (error) {
409
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
410
- }
411
- }
412
-
413
- // --- createSolidLayer (from createSolidLayer.jsx) ---
414
- function createSolidLayer(args) {
415
- try {
416
- var compName = args.compName || "";
417
- var color = args.color || [1, 1, 1];
418
- var name = args.name || "Solid Layer";
419
- var position = args.position || [960, 540];
420
- var size = args.size;
421
- var startTime = args.startTime || 0;
422
- var duration = args.duration || 5;
423
- var isAdjustment = args.isAdjustment || false;
424
- var comp = null;
425
- for (var i = 1; i <= app.project.numItems; i++) {
426
- var item = app.project.item(i);
427
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
428
- }
429
- if (!comp) {
430
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
431
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
432
- }
433
- if (!size) { size = [comp.width, comp.height]; }
434
- var solidLayer;
435
- if (isAdjustment) {
436
- solidLayer = comp.layers.addSolid([0, 0, 0], name, size[0], size[1], 1);
437
- solidLayer.adjustmentLayer = true;
438
- } else {
439
- solidLayer = comp.layers.addSolid(color, name, size[0], size[1], 1);
440
- }
441
- solidLayer.property("Position").setValue(position);
442
- solidLayer.startTime = startTime;
443
- if (duration > 0) { solidLayer.outPoint = startTime + duration; }
444
- return JSON.stringify({
445
- status: "success", message: isAdjustment ? "Adjustment layer created successfully" : "Solid layer created successfully",
446
- layer: { name: solidLayer.name, index: solidLayer.index, type: isAdjustment ? "adjustment" : "solid", inPoint: solidLayer.inPoint, outPoint: solidLayer.outPoint, position: solidLayer.property("Position").value, isAdjustment: solidLayer.adjustmentLayer }
447
- }, null, 2);
448
- } catch (error) {
449
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
450
- }
451
- }
452
-
453
- // --- setLayerProperties (modified to handle text properties) ---
454
- function setLayerProperties(args) {
455
- try {
456
- var compName = args.compName || "";
457
- var layerName = args.layerName || "";
458
- var layerIndex = args.layerIndex;
459
-
460
- // General Properties
461
- var position = args.position;
462
- var scale = args.scale;
463
- var rotation = args.rotation;
464
- var opacity = args.opacity;
465
- var startTime = args.startTime;
466
- var duration = args.duration;
467
-
468
- // Text Specific Properties
469
- var textContent = args.text; // New: text content
470
- var fontFamily = args.fontFamily; // New: font family
471
- var fontSize = args.fontSize; // New: font size
472
- var fillColor = args.fillColor; // New: font color
473
-
474
- // Find the composition (same logic as before)
475
- var comp = null;
476
- for (var i = 1; i <= app.project.numItems; i++) {
477
- var item = app.project.item(i);
478
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
479
- }
480
- if (!comp) {
481
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
482
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
483
- }
484
-
485
- // Find the layer (same logic as before)
486
- var layer = null;
487
- if (layerIndex !== undefined && layerIndex !== null) {
488
- if (layerIndex > 0 && layerIndex <= comp.numLayers) { layer = comp.layer(layerIndex); }
489
- else { throw new Error("Layer index out of bounds: " + layerIndex); }
490
- } else if (layerName) {
491
- for (var j = 1; j <= comp.numLayers; j++) {
492
- if (comp.layer(j).name === layerName) { layer = comp.layer(j); break; }
493
- }
494
- }
495
- if (!layer) { throw new Error("Layer not found: " + (layerName || "index " + layerIndex)); }
496
-
497
- var changedProperties = [];
498
- var textDocumentChanged = false;
499
- var textProp = null;
500
- var textDocument = null;
501
-
502
- // --- Text Property Handling ---
503
- if (layer instanceof TextLayer && (textContent !== undefined || fontFamily !== undefined || fontSize !== undefined || fillColor !== undefined)) {
504
- var sourceTextProp = layer.property("Source Text");
505
- if (sourceTextProp && sourceTextProp.value) {
506
- var currentTextDocument = sourceTextProp.value; // Get the current value
507
- var updated = false;
508
-
509
- if (textContent !== undefined && textContent !== null && currentTextDocument.text !== textContent) {
510
- currentTextDocument.text = textContent;
511
- changedProperties.push("text");
512
- updated = true;
513
- }
514
- if (fontFamily !== undefined && fontFamily !== null && currentTextDocument.font !== fontFamily) {
515
- // Add basic validation/logging for font existence if needed
516
- // try { app.fonts.findFont(fontFamily); } catch (e) { logToPanel("Warning: Font '"+fontFamily+"' might not be installed."); }
517
- currentTextDocument.font = fontFamily;
518
- changedProperties.push("fontFamily");
519
- updated = true;
520
- }
521
- if (fontSize !== undefined && fontSize !== null && currentTextDocument.fontSize !== fontSize) {
522
- currentTextDocument.fontSize = fontSize;
523
- changedProperties.push("fontSize");
524
- updated = true;
525
- }
526
- // Comparing colors needs care due to potential floating point inaccuracies if set via UI
527
- // Simple comparison for now
528
- if (fillColor !== undefined && fillColor !== null &&
529
- (currentTextDocument.fillColor[0] !== fillColor[0] ||
530
- currentTextDocument.fillColor[1] !== fillColor[1] ||
531
- currentTextDocument.fillColor[2] !== fillColor[2])) {
532
- currentTextDocument.fillColor = fillColor;
533
- changedProperties.push("fillColor");
534
- updated = true;
535
- }
536
-
537
- // Only set the value if something actually changed
538
- if (updated) {
539
- try {
540
- sourceTextProp.setValue(currentTextDocument);
541
- logToPanel("Applied changes to Text Document for layer: " + layer.name);
542
- } catch (e) {
543
- logToPanel("ERROR applying Text Document changes: " + e.toString());
544
- // Decide if we should throw or just log the error for text properties
545
- // For now, just log, other properties might still succeed
546
- }
547
- }
548
- // Store the potentially updated document for the return value
549
- textDocument = currentTextDocument;
550
-
551
- } else {
552
- logToPanel("Warning: Could not access Source Text property for layer: " + layer.name);
553
- }
554
- }
555
-
556
- // --- Enabled/Visible ---
557
- var enabled = args.enabled;
558
- if (enabled !== undefined && enabled !== null) { layer.enabled = !!enabled; changedProperties.push("enabled"); }
559
-
560
- // --- Blend Mode ---
561
- var blendMode = args.blendMode;
562
- if (blendMode !== undefined && blendMode !== null) {
563
- var modes = {
564
- "normal": BlendingMode.NORMAL,
565
- "add": BlendingMode.ADD,
566
- "multiply": BlendingMode.MULTIPLY,
567
- "screen": BlendingMode.SCREEN,
568
- "overlay": BlendingMode.OVERLAY,
569
- "softLight": BlendingMode.SOFT_LIGHT,
570
- "hardLight": BlendingMode.HARD_LIGHT,
571
- "colorDodge": BlendingMode.COLOR_DODGE,
572
- "colorBurn": BlendingMode.COLOR_BURN,
573
- "darken": BlendingMode.DARKEN,
574
- "lighten": BlendingMode.LIGHTEN,
575
- "difference": BlendingMode.DIFFERENCE,
576
- "exclusion": BlendingMode.EXCLUSION,
577
- "hue": BlendingMode.HUE,
578
- "saturation": BlendingMode.SATURATION,
579
- "color": BlendingMode.COLOR,
580
- "luminosity": BlendingMode.LUMINOSITY
581
- };
582
- if (modes[blendMode] !== undefined) {
583
- layer.blendingMode = modes[blendMode];
584
- changedProperties.push("blendMode");
585
- }
586
- }
587
-
588
- // --- Track Matte ---
589
- var trackMatteType = args.trackMatteType;
590
- if (trackMatteType !== undefined && trackMatteType !== null) {
591
- // Values: "none", "alpha", "alphaInverted", "luma", "lumaInverted"
592
- var matteTypes = {
593
- "none": TrackMatteType.NO_TRACK_MATTE,
594
- "alpha": TrackMatteType.ALPHA,
595
- "alphaInverted": TrackMatteType.ALPHA_INVERTED,
596
- "luma": TrackMatteType.LUMA,
597
- "lumaInverted": TrackMatteType.LUMA_INVERTED
598
- };
599
- if (matteTypes[trackMatteType] !== undefined) {
600
- layer.trackMatteType = matteTypes[trackMatteType];
601
- changedProperties.push("trackMatteType");
602
- }
603
- }
604
-
605
- // --- General Property Handling ---
606
- var threeDLayer = args.threeDLayer;
607
- if (threeDLayer !== undefined && threeDLayer !== null) { layer.threeDLayer = !!threeDLayer; changedProperties.push("threeDLayer"); }
608
- if (position !== undefined && position !== null) {
609
- var posProp = layer.property("Position");
610
- if (posProp.numKeys > 0) { while (posProp.numKeys > 0) { posProp.removeKey(1); } }
611
- posProp.setValue(position);
612
- changedProperties.push("position");
613
- }
614
- if (scale !== undefined && scale !== null) { layer.property("Scale").setValue(scale); changedProperties.push("scale"); }
615
- if (rotation !== undefined && rotation !== null) {
616
- if (layer.threeDLayer) {
617
- // For 3D layers, Z rotation is often what's intended by a single value
618
- layer.property("Z Rotation").setValue(rotation);
619
- } else {
620
- layer.property("Rotation").setValue(rotation);
621
- }
622
- changedProperties.push("rotation");
623
- }
624
- if (opacity !== undefined && opacity !== null) { layer.property("Opacity").setValue(opacity); changedProperties.push("opacity"); }
625
- if (startTime !== undefined && startTime !== null) { layer.startTime = startTime; changedProperties.push("startTime"); }
626
- if (duration !== undefined && duration !== null && duration > 0) {
627
- var actualStartTime = (startTime !== undefined && startTime !== null) ? startTime : layer.startTime;
628
- layer.outPoint = actualStartTime + duration;
629
- changedProperties.push("duration");
630
- }
631
-
632
- // Return success with updated layer details (including text if changed)
633
- var returnLayerInfo = {
634
- name: layer.name,
635
- index: layer.index,
636
- threeDLayer: layer.threeDLayer,
637
- position: layer.property("Position").value,
638
- scale: layer.property("Scale").value,
639
- rotation: layer.threeDLayer ? layer.property("Z Rotation").value : layer.property("Rotation").value, // Return appropriate rotation
640
- opacity: layer.property("Opacity").value,
641
- inPoint: layer.inPoint,
642
- outPoint: layer.outPoint,
643
- changedProperties: changedProperties
644
- };
645
- // Add text properties to the return object if it was a text layer
646
- if (layer instanceof TextLayer && textDocument) {
647
- returnLayerInfo.text = textDocument.text;
648
- returnLayerInfo.fontFamily = textDocument.font;
649
- returnLayerInfo.fontSize = textDocument.fontSize;
650
- returnLayerInfo.fillColor = textDocument.fillColor;
651
- }
652
-
653
- // *** ADDED LOGGING HERE ***
654
- logToPanel("Final check before return:");
655
- logToPanel(" Changed Properties: " + changedProperties.join(", "));
656
- logToPanel(" Return Layer Info Font: " + (returnLayerInfo.fontFamily || "N/A"));
657
- logToPanel(" TextDocument Font: " + (textDocument ? textDocument.font : "N/A"));
658
-
659
- return JSON.stringify({
660
- status: "success", message: "Layer properties updated successfully",
661
- layer: returnLayerInfo
662
- }, null, 2);
663
- } catch (error) {
664
- // Error handling remains similar, but add more specific checks if needed
665
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
666
- }
667
- }
668
-
669
- // --- batchSetLayerProperties: apply properties to multiple layers in one call ---
670
- function batchSetLayerProperties(args) {
671
- try {
672
- var compName = args.compName || "";
673
- var operations = args.operations; // Array of {layerIndex, threeDLayer, position, scale, rotation, opacity, ...}
674
-
675
- if (!operations || !operations.length) {
676
- throw new Error("No operations provided. Pass an array of {layerIndex, ...properties}");
677
- }
678
-
679
- var comp = null;
680
- for (var i = 1; i <= app.project.numItems; i++) {
681
- var item = app.project.item(i);
682
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
683
- }
684
- if (!comp) {
685
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
686
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
687
- }
688
-
689
- var results = [];
690
- for (var o = 0; o < operations.length; o++) {
691
- var op = operations[o];
692
- var layer = null;
693
- if (op.layerIndex !== undefined && op.layerIndex !== null) {
694
- if (op.layerIndex > 0 && op.layerIndex <= comp.numLayers) { layer = comp.layer(op.layerIndex); }
695
- else { results.push({ layerIndex: op.layerIndex, status: "error", message: "Layer index out of bounds" }); continue; }
696
- } else if (op.layerName) {
697
- for (var j = 1; j <= comp.numLayers; j++) {
698
- if (comp.layer(j).name === op.layerName) { layer = comp.layer(j); break; }
699
- }
700
- }
701
- if (!layer) { results.push({ layerIndex: op.layerIndex, layerName: op.layerName, status: "error", message: "Layer not found" }); continue; }
702
-
703
- var changed = [];
704
- if (op.threeDLayer !== undefined && op.threeDLayer !== null) { layer.threeDLayer = !!op.threeDLayer; changed.push("threeDLayer"); }
705
- if (op.position !== undefined && op.position !== null) {
706
- var posProp = layer.property("Position");
707
- if (posProp.numKeys > 0) {
708
- while (posProp.numKeys > 0) { posProp.removeKey(1); }
709
- }
710
- posProp.setValue(op.position);
711
- changed.push("position");
712
- }
713
- if (op.scale !== undefined && op.scale !== null) { layer.property("Scale").setValue(op.scale); changed.push("scale"); }
714
- if (op.rotation !== undefined && op.rotation !== null) {
715
- if (layer.threeDLayer) { layer.property("Z Rotation").setValue(op.rotation); }
716
- else { layer.property("Rotation").setValue(op.rotation); }
717
- changed.push("rotation");
718
- }
719
- if (op.opacity !== undefined && op.opacity !== null) { layer.property("Opacity").setValue(op.opacity); changed.push("opacity"); }
720
- if (op.blendMode !== undefined && op.blendMode !== null) {
721
- var bModes = {"normal":BlendingMode.NORMAL,"add":BlendingMode.ADD,"multiply":BlendingMode.MULTIPLY,"screen":BlendingMode.SCREEN,"overlay":BlendingMode.OVERLAY,"softLight":BlendingMode.SOFT_LIGHT,"hardLight":BlendingMode.HARD_LIGHT,"darken":BlendingMode.DARKEN,"lighten":BlendingMode.LIGHTEN,"difference":BlendingMode.DIFFERENCE};
722
- if (bModes[op.blendMode] !== undefined) { layer.blendingMode = bModes[op.blendMode]; changed.push("blendMode"); }
723
- }
724
- if (op.startTime !== undefined && op.startTime !== null) { layer.startTime = op.startTime; changed.push("startTime"); }
725
- if (op.outPoint !== undefined && op.outPoint !== null) { layer.outPoint = op.outPoint; changed.push("outPoint"); }
726
-
727
- results.push({
728
- layerIndex: layer.index,
729
- name: layer.name,
730
- status: "success",
731
- threeDLayer: layer.threeDLayer,
732
- position: layer.property("Position").value,
733
- changedProperties: changed
734
- });
735
- }
736
-
737
- return JSON.stringify({ status: "success", results: results }, null, 2);
738
- } catch (error) {
739
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
740
- }
741
- }
742
-
743
- /**
744
- * Sets a keyframe for a specific property on a layer.
745
- * Indices are 1-based for After Effects collections.
746
- * @param {number} compIndex - The index of the composition (1-based).
747
- * @param {number} layerIndex - The index of the layer within the composition (1-based).
748
- * @param {string} propertyName - The name of the property (e.g., "Position", "Scale", "Rotation", "Opacity").
749
- * @param {number} timeInSeconds - The time (in seconds) for the keyframe.
750
- * @param {any} value - The value for the keyframe (e.g., [x, y] for Position, [w, h] for Scale, angle for Rotation, percentage for Opacity).
751
- * @returns {string} JSON string indicating success or error.
752
- */
753
- function setLayerKeyframe(compIndex, layerIndex, propertyName, timeInSeconds, value) {
754
- try {
755
- // Use 1-based indices as per After Effects API
756
- var comp = app.project.items[compIndex];
757
- if (!comp || !(comp instanceof CompItem)) {
758
- return JSON.stringify({ success: false, message: "Composition not found at index " + compIndex });
759
- }
760
- var layer = comp.layers[layerIndex];
761
- if (!layer) {
762
- return JSON.stringify({ success: false, message: "Layer not found at index " + layerIndex + " in composition '" + comp.name + "'"});
763
- }
764
-
765
- var transformGroup = layer.property("Transform");
766
- if (!transformGroup) {
767
- return JSON.stringify({ success: false, message: "Transform properties not found for layer '" + layer.name + "' (type: " + layer.matchName + ")." });
768
- }
769
-
770
- var property = transformGroup.property(propertyName);
771
- if (!property) {
772
- // Check other common property groups if not in Transform
773
- if (layer.property("Effects") && layer.property("Effects").property(propertyName)) {
774
- property = layer.property("Effects").property(propertyName);
775
- } else if (layer.property("Text") && layer.property("Text").property(propertyName)) {
776
- property = layer.property("Text").property(propertyName);
777
- } // Add more groups if needed (e.g., Masks, Shapes)
778
-
779
- if (!property) {
780
- return JSON.stringify({ success: false, message: "Property '" + propertyName + "' not found on layer '" + layer.name + "'." });
781
- }
782
- }
783
-
784
-
785
- // Ensure the property can be keyframed
786
- if (!property.canVaryOverTime) {
787
- return JSON.stringify({ success: false, message: "Property '" + propertyName + "' cannot be keyframed." });
788
- }
789
-
790
- // Make sure the property is enabled for keyframing
791
- if (property.numKeys === 0 && !property.isTimeVarying) {
792
- property.setValueAtTime(comp.time, property.value); // Set initial keyframe if none exist
793
- }
794
-
795
-
796
- property.setValueAtTime(timeInSeconds, value);
797
-
798
- return JSON.stringify({ success: true, message: "Keyframe set for '" + propertyName + "' on layer '" + layer.name + "' at " + timeInSeconds + "s." });
799
- } catch (e) {
800
- return JSON.stringify({ success: false, message: "Error setting keyframe: " + e.toString() + " (Line: " + e.line + ")" });
801
- }
802
- }
803
-
804
-
805
- /**
806
- * Sets an expression for a specific property on a layer.
807
- * @param {number} compIndex - The index of the composition (1-based).
808
- * @param {number} layerIndex - The index of the layer within the composition (1-based).
809
- * @param {string} propertyName - The name of the property (e.g., "Position", "Scale", "Rotation", "Opacity").
810
- * @param {string} expressionString - The JavaScript expression string. Use "" to remove expression.
811
- * @returns {string} JSON string indicating success or error.
812
- */
813
- function setLayerExpression(compIndex, layerIndex, propertyName, expressionString) {
814
- try {
815
- // Adjust indices to be 0-based for ExtendScript arrays
816
- var comp = app.project.items[compIndex];
817
- if (!comp || !(comp instanceof CompItem)) {
818
- return JSON.stringify({ success: false, message: "Composition not found at index " + compIndex });
819
- }
820
- var layer = comp.layers[layerIndex];
821
- if (!layer) {
822
- return JSON.stringify({ success: false, message: "Layer not found at index " + layerIndex + " in composition '" + comp.name + "'"});
823
- }
824
-
825
- var transformGroup = layer.property("Transform");
826
- if (!transformGroup) {
827
- // Allow expressions on non-transformable layers if property exists elsewhere
828
- // return JSON.stringify({ success: false, message: "Transform properties not found for layer '" + layer.name + "' (type: " + layer.matchName + ")." });
829
- }
830
-
831
- var property = transformGroup ? transformGroup.property(propertyName) : null;
832
- if (!property) {
833
- // Check other common property groups if not in Transform
834
- if (layer.property("Effects") && layer.property("Effects").property(propertyName)) {
835
- property = layer.property("Effects").property(propertyName);
836
- } else if (layer.property("Text") && layer.property("Text").property(propertyName)) {
837
- property = layer.property("Text").property(propertyName);
838
- }
839
-
840
- // Search inside individual effects for sub-properties
841
- if (!property && layer.property("Effects")) {
842
- var effects = layer.property("Effects");
843
- for (var ei = 1; ei <= effects.numProperties; ei++) {
844
- var eff = effects.property(ei);
845
- try {
846
- var subProp = eff.property(propertyName);
847
- if (subProp) { property = subProp; break; }
848
- } catch (e2) {}
849
- }
850
- }
851
-
852
- if (!property) {
853
- return JSON.stringify({ success: false, message: "Property '" + propertyName + "' not found on layer '" + layer.name + "'." });
854
- }
855
- }
856
-
857
- if (!property.canSetExpression) {
858
- return JSON.stringify({ success: false, message: "Property '" + propertyName + "' does not support expressions." });
859
- }
860
-
861
- property.expression = expressionString;
862
-
863
- var action = expressionString === "" ? "removed" : "set";
864
- return JSON.stringify({ success: true, message: "Expression " + action + " for '" + propertyName + "' on layer '" + layer.name + "'." });
865
- } catch (e) {
866
- return JSON.stringify({ success: false, message: "Error setting expression: " + e.toString() + " (Line: " + e.line + ")" });
867
- }
868
- }
869
-
870
- // --- applyEffect (from applyEffect.jsx) ---
871
- function applyEffect(args) {
872
- try {
873
- // Extract parameters
874
- var compIndex = args.compIndex || 1; // Default to first comp
875
- var layerIndex = args.layerIndex || 1; // Default to first layer
876
- var effectName = args.effectName; // Name of the effect to apply
877
- var effectMatchName = args.effectMatchName; // After Effects internal name (more reliable)
878
- var effectCategory = args.effectCategory || ""; // Optional category for filtering
879
- var presetPath = args.presetPath; // Optional path to an effect preset
880
- var effectSettings = args.effectSettings || {}; // Optional effect parameters
881
-
882
- if (!effectName && !effectMatchName && !presetPath) {
883
- throw new Error("You must specify either effectName, effectMatchName, or presetPath");
884
- }
885
-
886
- // Find the composition by index
887
- var comp = app.project.item(compIndex);
888
- if (!comp || !(comp instanceof CompItem)) {
889
- throw new Error("Composition not found at index " + compIndex);
890
- }
891
-
892
- // Find the layer by index
893
- var layer = comp.layer(layerIndex);
894
- if (!layer) {
895
- throw new Error("Layer not found at index " + layerIndex + " in composition '" + comp.name + "'");
896
- }
897
-
898
- var effectResult;
899
-
900
- // Apply preset if a path is provided
901
- if (presetPath) {
902
- var presetFile = new File(presetPath);
903
- if (!presetFile.exists) {
904
- throw new Error("Effect preset file not found: " + presetPath);
905
- }
906
-
907
- // Apply the preset to the layer
908
- layer.applyPreset(presetFile);
909
- effectResult = {
910
- type: "preset",
911
- name: presetPath.split('/').pop().split('\\').pop(),
912
- applied: true
913
- };
914
- }
915
- // Apply effect by match name (more reliable method)
916
- else if (effectMatchName) {
917
- var effect = layer.Effects.addProperty(effectMatchName);
918
- effectResult = {
919
- type: "effect",
920
- name: effect.name,
921
- matchName: effect.matchName,
922
- index: effect.propertyIndex
923
- };
924
-
925
- // Apply settings if provided
926
- applyEffectSettings(effect, effectSettings);
927
- }
928
- // Apply effect by display name
929
- else {
930
- // Get the effect from the Effect menu
931
- var effect = layer.Effects.addProperty(effectName);
932
- effectResult = {
933
- type: "effect",
934
- name: effect.name,
935
- matchName: effect.matchName,
936
- index: effect.propertyIndex
937
- };
938
-
939
- // Apply settings if provided
940
- applyEffectSettings(effect, effectSettings);
941
- }
942
-
943
- return JSON.stringify({
944
- status: "success",
945
- message: "Effect applied successfully",
946
- effect: effectResult,
947
- layer: {
948
- name: layer.name,
949
- index: layerIndex
950
- },
951
- composition: {
952
- name: comp.name,
953
- index: compIndex
954
- }
955
- }, null, 2);
956
- } catch (error) {
957
- return JSON.stringify({
958
- status: "error",
959
- message: error.toString()
960
- }, null, 2);
961
- }
962
- }
963
-
964
- // Helper function to apply effect settings
965
- function applyEffectSettings(effect, settings) {
966
- // Skip if no settings are provided
967
- if (!settings) return;
968
- var hasKeys = false;
969
- for (var k in settings) { if (settings.hasOwnProperty(k)) { hasKeys = true; break; } }
970
- if (!hasKeys) return;
971
-
972
- // Iterate through all provided settings
973
- for (var propName in settings) {
974
- if (settings.hasOwnProperty(propName)) {
975
- try {
976
- // Find the property in the effect
977
- var property = null;
978
-
979
- // Try direct property access first
980
- try {
981
- property = effect.property(propName);
982
- } catch (e) {
983
- // If direct access fails, search through all properties
984
- for (var i = 1; i <= effect.numProperties; i++) {
985
- var prop = effect.property(i);
986
- if (prop.name === propName) {
987
- property = prop;
988
- break;
989
- }
990
- }
991
- }
992
-
993
- // Set the property value if found
994
- if (property && property.setValue) {
995
- property.setValue(settings[propName]);
996
- }
997
- } catch (e) {
998
- // Log error but continue with other properties
999
- $.writeln("Error setting effect property '" + propName + "': " + e.toString());
1000
- }
1001
- }
1002
- }
1003
- }
1004
-
1005
- // --- applyEffectTemplate (from applyEffectTemplate.jsx) ---
1006
- function applyEffectTemplate(args) {
1007
- try {
1008
- // Extract parameters
1009
- var compIndex = args.compIndex || 1; // Default to first comp
1010
- var layerIndex = args.layerIndex || 1; // Default to first layer
1011
- var templateName = args.templateName; // Name of the template to apply
1012
- var customSettings = args.customSettings || {}; // Optional customizations
1013
-
1014
- if (!templateName) {
1015
- throw new Error("You must specify a templateName");
1016
- }
1017
-
1018
- // Find the composition by index
1019
- var comp = app.project.item(compIndex);
1020
- if (!comp || !(comp instanceof CompItem)) {
1021
- throw new Error("Composition not found at index " + compIndex);
1022
- }
1023
-
1024
- // Find the layer by index
1025
- var layer = comp.layer(layerIndex);
1026
- if (!layer) {
1027
- throw new Error("Layer not found at index " + layerIndex + " in composition '" + comp.name + "'");
1028
- }
1029
-
1030
- // Template definitions
1031
- var templates = {
1032
- // Blur effects
1033
- "gaussian-blur": {
1034
- effectMatchName: "ADBE Gaussian Blur 2",
1035
- settings: {
1036
- "Blurriness": customSettings.blurriness || 20
1037
- }
1038
- },
1039
- "directional-blur": {
1040
- effectMatchName: "ADBE Directional Blur",
1041
- settings: {
1042
- "Direction": customSettings.direction || 0,
1043
- "Blur Length": customSettings.length || 10
1044
- }
1045
- },
1046
-
1047
- // Color correction effects
1048
- "color-balance": {
1049
- effectMatchName: "ADBE Color Balance (HLS)",
1050
- settings: {
1051
- "Hue": customSettings.hue || 0,
1052
- "Lightness": customSettings.lightness || 0,
1053
- "Saturation": customSettings.saturation || 0
1054
- }
1055
- },
1056
- "brightness-contrast": {
1057
- effectMatchName: "ADBE Brightness & Contrast 2",
1058
- settings: {
1059
- "Brightness": customSettings.brightness || 0,
1060
- "Contrast": customSettings.contrast || 0,
1061
- "Use Legacy": false
1062
- }
1063
- },
1064
- "curves": {
1065
- effectMatchName: "ADBE CurvesCustom",
1066
- // Curves are complex and would need special handling
1067
- },
1068
-
1069
- // Stylistic effects
1070
- "glow": {
1071
- effectMatchName: "ADBE Glow",
1072
- settings: {
1073
- "Glow Threshold": customSettings.threshold || 50,
1074
- "Glow Radius": customSettings.radius || 15,
1075
- "Glow Intensity": customSettings.intensity || 1
1076
- }
1077
- },
1078
- "drop-shadow": {
1079
- effectMatchName: "ADBE Drop Shadow",
1080
- settings: {
1081
- "Shadow Color": customSettings.color || [0, 0, 0, 1],
1082
- "Opacity": customSettings.opacity || 50,
1083
- "Direction": customSettings.direction || 135,
1084
- "Distance": customSettings.distance || 10,
1085
- "Softness": customSettings.softness || 10
1086
- }
1087
- },
1088
-
1089
- // Common effect chains
1090
- "cinematic-look": {
1091
- effects: [
1092
- {
1093
- effectMatchName: "ADBE CurvesCustom",
1094
- settings: {}
1095
- },
1096
- {
1097
- effectMatchName: "ADBE Vibrance",
1098
- settings: {
1099
- "Vibrance": 15,
1100
- "Saturation": -5
1101
- }
1102
- }
1103
- ]
1104
- },
1105
- "text-pop": {
1106
- effects: [
1107
- {
1108
- effectMatchName: "ADBE Drop Shadow",
1109
- settings: {
1110
- "Shadow Color": [0, 0, 0, 1],
1111
- "Opacity": 75,
1112
- "Distance": 5,
1113
- "Softness": 10
1114
- }
1115
- },
1116
- {
1117
- effectMatchName: "ADBE Glow",
1118
- settings: {
1119
- "Glow Threshold": 50,
1120
- "Glow Radius": 10,
1121
- "Glow Intensity": 1.5
1122
- }
1123
- }
1124
- ]
1125
- }
1126
- };
1127
-
1128
- // Check if the requested template exists
1129
- var template = templates[templateName];
1130
- if (!template) {
1131
- var availableTemplates = Object.keys(templates).join(", ");
1132
- throw new Error("Template '" + templateName + "' not found. Available templates: " + availableTemplates);
1133
- }
1134
-
1135
- var appliedEffects = [];
1136
-
1137
- // Apply single effect or multiple effects based on template structure
1138
- if (template.effectMatchName) {
1139
- // Single effect template
1140
- var effect = layer.Effects.addProperty(template.effectMatchName);
1141
-
1142
- // Apply settings
1143
- for (var propName in template.settings) {
1144
- try {
1145
- var property = effect.property(propName);
1146
- if (property) {
1147
- property.setValue(template.settings[propName]);
1148
- }
1149
- } catch (e) {
1150
- $.writeln("Warning: Could not set " + propName + " on effect " + effect.name + ": " + e);
1151
- }
1152
- }
1153
-
1154
- appliedEffects.push({
1155
- name: effect.name,
1156
- matchName: effect.matchName
1157
- });
1158
- } else if (template.effects) {
1159
- // Multiple effects template
1160
- for (var i = 0; i < template.effects.length; i++) {
1161
- var effectData = template.effects[i];
1162
- var effect = layer.Effects.addProperty(effectData.effectMatchName);
1163
-
1164
- // Apply settings
1165
- for (var propName in effectData.settings) {
1166
- try {
1167
- var property = effect.property(propName);
1168
- if (property) {
1169
- property.setValue(effectData.settings[propName]);
1170
- }
1171
- } catch (e) {
1172
- $.writeln("Warning: Could not set " + propName + " on effect " + effect.name + ": " + e);
1173
- }
1174
- }
1175
-
1176
- appliedEffects.push({
1177
- name: effect.name,
1178
- matchName: effect.matchName
1179
- });
1180
- }
1181
- }
1182
-
1183
- return JSON.stringify({
1184
- status: "success",
1185
- message: "Effect template '" + templateName + "' applied successfully",
1186
- appliedEffects: appliedEffects,
1187
- layer: {
1188
- name: layer.name,
1189
- index: layerIndex
1190
- },
1191
- composition: {
1192
- name: comp.name,
1193
- index: compIndex
1194
- }
1195
- }, null, 2);
1196
- } catch (error) {
1197
- return JSON.stringify({
1198
- status: "error",
1199
- message: error.toString()
1200
- }, null, 2);
1201
- }
1202
- }
1203
-
1204
- // --- End of Function Definitions ---
1205
-
1206
- // --- Bridge test function to verify communication and effects application ---
1207
- function bridgeTestEffects(args) {
1208
- try {
1209
- var compIndex = (args && args.compIndex) ? args.compIndex : 1;
1210
- var layerIndex = (args && args.layerIndex) ? args.layerIndex : 1;
1211
-
1212
- // Apply a light Gaussian Blur
1213
- var blurRes = JSON.parse(applyEffect({
1214
- compIndex: compIndex,
1215
- layerIndex: layerIndex,
1216
- effectMatchName: "ADBE Gaussian Blur 2",
1217
- effectSettings: { "Blurriness": 5 }
1218
- }));
1219
-
1220
- // Apply a simple drop shadow via template
1221
- var shadowRes = JSON.parse(applyEffectTemplate({
1222
- compIndex: compIndex,
1223
- layerIndex: layerIndex,
1224
- templateName: "drop-shadow"
1225
- }));
1226
-
1227
- return JSON.stringify({
1228
- status: "success",
1229
- message: "Bridge test effects applied.",
1230
- results: [blurRes, shadowRes]
1231
- }, null, 2);
1232
- } catch (e) {
1233
- return JSON.stringify({ status: "error", message: e.toString() }, null, 2);
1234
- }
1235
- }
1236
-
1237
- // JSON polyfill for ExtendScript (when JSON is undefined)
1238
- if (typeof JSON === "undefined") {
1239
- JSON = {};
1240
- }
1241
- if (typeof JSON.parse !== "function") {
1242
- JSON.parse = function (text) {
1243
- // Safe-ish fallback for trusted input (our own command file)
1244
- return eval("(" + text + ")");
1245
- };
1246
- }
1247
- if (typeof JSON.stringify !== "function") {
1248
- (function () {
1249
- function esc(str) {
1250
- return (str + "")
1251
- .replace(/\\/g, "\\\\")
1252
- .replace(/"/g, '\\"')
1253
- .replace(/\n/g, "\\n")
1254
- .replace(/\r/g, "\\r")
1255
- .replace(/\t/g, "\\t");
1256
- }
1257
- function toJSON(val) {
1258
- if (val === null) return "null";
1259
- var t = typeof val;
1260
- if (t === "number" || t === "boolean") return String(val);
1261
- if (t === "string") return '"' + esc(val) + '"';
1262
- if (val instanceof Array) {
1263
- var a = [];
1264
- for (var i = 0; i < val.length; i++) a.push(toJSON(val[i]));
1265
- return "[" + a.join(",") + "]";
1266
- }
1267
- if (t === "object") {
1268
- var props = [];
1269
- for (var k in val) {
1270
- if (val.hasOwnProperty(k) && typeof val[k] !== "function" && typeof val[k] !== "undefined") {
1271
- props.push('"' + esc(k) + '":' + toJSON(val[k]));
1272
- }
1273
- }
1274
- return "{" + props.join(",") + "}";
1275
- }
1276
- return "null";
1277
- }
1278
- JSON.stringify = function (value, _replacer, _space) {
1279
- return toJSON(value);
1280
- };
1281
- })();
1282
- }
1283
-
1284
- // Detect AE version (AE 2025 = version 25.x, AE 2026 = version 26.x)
1285
- var aeVersion = parseFloat(app.version);
1286
- var isAE2025OrLater = aeVersion >= 25.0;
1287
-
1288
- // Always create a floating palette window for AE 2025+
1289
- var panel = new Window("palette", "MCP Bridge Auto", undefined);
1290
- panel.orientation = "column";
1291
- panel.alignChildren = ["fill", "top"];
1292
- panel.spacing = 10;
1293
- panel.margins = 16;
1294
-
1295
- // Status display
1296
- var statusText = panel.add("statictext", undefined, "Waiting for commands...");
1297
- statusText.alignment = ["fill", "top"];
1298
-
1299
- // Add log area
1300
- var logPanel = panel.add("panel", undefined, "Command Log");
1301
- logPanel.orientation = "column";
1302
- logPanel.alignChildren = ["fill", "fill"];
1303
- var logText = logPanel.add("edittext", undefined, "", {multiline: true, readonly: true});
1304
- logText.preferredSize.height = 200;
1305
-
1306
- // AE 2025 warning
1307
- if (isAE2025OrLater) {
1308
- var warning = panel.add("statictext", undefined, "AE 2025+: Dockable panels are not supported. Floating window only.");
1309
- warning.graphics.foregroundColor = warning.graphics.newPen(warning.graphics.PenType.SOLID_COLOR, [1,0.3,0,1], 1);
1310
- }
1311
-
1312
- // Auto-run checkbox
1313
- var autoRunCheckbox = panel.add("checkbox", undefined, "Auto-run commands");
1314
- autoRunCheckbox.value = true;
1315
-
1316
- // Check interval (ms)
1317
- var checkInterval = 2000;
1318
- var isChecking = false;
1319
-
1320
- // Command file path - use Documents folder for reliable access
1321
- function getCommandFilePath() {
1322
- var userFolder = Folder.myDocuments;
1323
- var bridgeFolder = new Folder(userFolder.fsName + "/ae-mcp-bridge");
1324
- if (!bridgeFolder.exists) {
1325
- bridgeFolder.create();
1326
- }
1327
- return bridgeFolder.fsName + "/ae_command.json";
1328
- }
1329
-
1330
- // Result file path - use Documents folder for reliable access
1331
- function getResultFilePath() {
1332
- var userFolder = Folder.myDocuments;
1333
- var bridgeFolder = new Folder(userFolder.fsName + "/ae-mcp-bridge");
1334
- if (!bridgeFolder.exists) {
1335
- bridgeFolder.create();
1336
- }
1337
- return bridgeFolder.fsName + "/ae_mcp_result.json";
1338
- }
1339
-
1340
- // --- setCompositionProperties: set duration, frameRate, etc. on active or named comp ---
1341
- function setCompositionProperties(args) {
1342
- try {
1343
- var compName = args.compName || "";
1344
- var comp = null;
1345
- for (var i = 1; i <= app.project.numItems; i++) {
1346
- var item = app.project.item(i);
1347
- if (item instanceof CompItem && item.name === compName) { comp = item; break; }
1348
- }
1349
- if (!comp) {
1350
- if (app.project.activeItem instanceof CompItem) { comp = app.project.activeItem; }
1351
- else { throw new Error("No composition found with name '" + compName + "' and no active composition"); }
1352
- }
1353
- var changed = [];
1354
- if (args.duration !== undefined && args.duration !== null) { comp.duration = args.duration; changed.push("duration"); }
1355
- if (args.frameRate !== undefined && args.frameRate !== null) { comp.frameRate = args.frameRate; changed.push("frameRate"); }
1356
- if (args.width !== undefined && args.width !== null && args.height !== undefined && args.height !== null) {
1357
- comp.width = args.width; comp.height = args.height; changed.push("dimensions");
1358
- }
1359
- return JSON.stringify({
1360
- status: "success",
1361
- composition: { name: comp.name, duration: comp.duration, frameRate: comp.frameRate, width: comp.width, height: comp.height },
1362
- changedProperties: changed
1363
- }, null, 2);
1364
- } catch (error) {
1365
- return JSON.stringify({ status: "error", message: error.toString() }, null, 2);
1366
- }
1367
- }
1368
-
1369
- // Functions for each script type
1370
- function getProjectInfo() {
1371
- var project = app.project;
1372
- var result = {
1373
- projectName: project.file ? project.file.name : "Untitled Project",
1374
- path: project.file ? project.file.fsName : "",
1375
- numItems: project.numItems,
1376
- bitsPerChannel: project.bitsPerChannel,
1377
- timeMode: project.timeDisplayType === TimeDisplayType.FRAMES ? "Frames" : "Timecode",
1378
- items: []
1379
- };
1380
-
1381
- // Count item types
1382
- var countByType = {
1383
- compositions: 0,
1384
- footage: 0,
1385
- folders: 0,
1386
- solids: 0
1387
- };
1388
-
1389
- // Get item information (limited for performance)
1390
- for (var i = 1; i <= Math.min(project.numItems, 50); i++) {
1391
- var item = project.item(i);
1392
- var itemType = "";
1393
-
1394
- if (item instanceof CompItem) {
1395
- itemType = "Composition";
1396
- countByType.compositions++;
1397
- } else if (item instanceof FolderItem) {
1398
- itemType = "Folder";
1399
- countByType.folders++;
1400
- } else if (item instanceof FootageItem) {
1401
- if (item.mainSource instanceof SolidSource) {
1402
- itemType = "Solid";
1403
- countByType.solids++;
1404
- } else {
1405
- itemType = "Footage";
1406
- countByType.footage++;
1407
- }
1408
- }
1409
-
1410
- result.items.push({
1411
- id: item.id,
1412
- name: item.name,
1413
- type: itemType
1414
- });
1415
- }
1416
-
1417
- result.itemCounts = countByType;
1418
-
1419
- // Include active composition metadata if available
1420
- if (app.project.activeItem instanceof CompItem) {
1421
- var ac = app.project.activeItem;
1422
- result.activeComp = {
1423
- id: ac.id,
1424
- name: ac.name,
1425
- width: ac.width,
1426
- height: ac.height,
1427
- duration: ac.duration,
1428
- frameRate: ac.frameRate,
1429
- numLayers: ac.numLayers
1430
- };
1431
- }
1432
-
1433
- return JSON.stringify(result, null, 2);
1434
- }
1435
-
1436
- function listCompositions() {
1437
- var project = app.project;
1438
- var result = {
1439
- compositions: []
1440
- };
1441
-
1442
- // Loop through items in the project
1443
- for (var i = 1; i <= project.numItems; i++) {
1444
- var item = project.item(i);
1445
-
1446
- // Check if the item is a composition
1447
- if (item instanceof CompItem) {
1448
- result.compositions.push({
1449
- id: item.id,
1450
- name: item.name,
1451
- duration: item.duration,
1452
- frameRate: item.frameRate,
1453
- width: item.width,
1454
- height: item.height,
1455
- numLayers: item.numLayers
1456
- });
1457
- }
1458
- }
1459
-
1460
- return JSON.stringify(result, null, 2);
1461
- }
1462
-
1463
- function getLayerInfo() {
1464
- var project = app.project;
1465
- var result = {
1466
- layers: []
1467
- };
1468
-
1469
- // Get the active composition
1470
- var activeComp = null;
1471
- if (app.project.activeItem instanceof CompItem) {
1472
- activeComp = app.project.activeItem;
1473
- } else {
1474
- return JSON.stringify({ error: "No active composition" }, null, 2);
1475
- }
1476
-
1477
- // Loop through layers in the active composition
1478
- for (var i = 1; i <= activeComp.numLayers; i++) {
1479
- var layer = activeComp.layer(i);
1480
- var layerInfo = {
1481
- index: layer.index,
1482
- name: layer.name,
1483
- enabled: layer.enabled,
1484
- locked: layer.locked,
1485
- threeDLayer: layer.threeDLayer,
1486
- position: layer.property("Position").value,
1487
- inPoint: layer.inPoint,
1488
- outPoint: layer.outPoint
1489
- };
1490
-
1491
- result.layers.push(layerInfo);
1492
- }
1493
-
1494
- return JSON.stringify(result, null, 2);
1495
- }
1496
-
1497
- // Execute command
1498
- function executeCommand(command, args) {
1499
- var result = "";
1500
-
1501
- logToPanel("Executing command: " + command);
1502
- statusText.text = "Running: " + command;
1503
- panel.update();
1504
-
1505
- try {
1506
- logToPanel("Attempting to execute: " + command); // Log before switch
1507
- // Use a switch statement for clarity
1508
- switch (command) {
1509
- case "getProjectInfo":
1510
- result = getProjectInfo();
1511
- break;
1512
- case "listCompositions":
1513
- result = listCompositions();
1514
- break;
1515
- case "getLayerInfo":
1516
- result = getLayerInfo();
1517
- break;
1518
- case "createComposition":
1519
- logToPanel("Calling createComposition function...");
1520
- result = createComposition(args);
1521
- logToPanel("Returned from createComposition.");
1522
- break;
1523
- case "createTextLayer":
1524
- logToPanel("Calling createTextLayer function...");
1525
- result = createTextLayer(args);
1526
- logToPanel("Returned from createTextLayer.");
1527
- break;
1528
- case "createShapeLayer":
1529
- logToPanel("Calling createShapeLayer function...");
1530
- result = createShapeLayer(args);
1531
- logToPanel("Returned from createShapeLayer. Result type: " + typeof result);
1532
- break;
1533
- case "createSolidLayer":
1534
- logToPanel("Calling createSolidLayer function...");
1535
- result = createSolidLayer(args);
1536
- logToPanel("Returned from createSolidLayer.");
1537
- break;
1538
- case "setLayerProperties":
1539
- logToPanel("Calling setLayerProperties function...");
1540
- result = setLayerProperties(args);
1541
- logToPanel("Returned from setLayerProperties.");
1542
- break;
1543
- case "setLayerKeyframe":
1544
- logToPanel("Calling setLayerKeyframe function...");
1545
- result = setLayerKeyframe(args.compIndex, args.layerIndex, args.propertyName, args.timeInSeconds, args.value);
1546
- logToPanel("Returned from setLayerKeyframe.");
1547
- break;
1548
- case "setLayerExpression":
1549
- logToPanel("Calling setLayerExpression function...");
1550
- result = setLayerExpression(args.compIndex, args.layerIndex, args.propertyName, args.expressionString);
1551
- logToPanel("Returned from setLayerExpression.");
1552
- break;
1553
- case "applyEffect":
1554
- logToPanel("Calling applyEffect function...");
1555
- result = applyEffect(args);
1556
- logToPanel("Returned from applyEffect.");
1557
- break;
1558
- case "applyEffectTemplate":
1559
- logToPanel("Calling applyEffectTemplate function...");
1560
- result = applyEffectTemplate(args);
1561
- logToPanel("Returned from applyEffectTemplate.");
1562
- break;
1563
- case "bridgeTestEffects":
1564
- logToPanel("Calling bridgeTestEffects function...");
1565
- result = bridgeTestEffects(args);
1566
- logToPanel("Returned from bridgeTestEffects.");
1567
- break;
1568
- case "createCamera":
1569
- logToPanel("Calling createCamera function...");
1570
- result = createCamera(args);
1571
- logToPanel("Returned from createCamera.");
1572
- break;
1573
- case "batchSetLayerProperties":
1574
- logToPanel("Calling batchSetLayerProperties function...");
1575
- result = batchSetLayerProperties(args);
1576
- logToPanel("Returned from batchSetLayerProperties.");
1577
- break;
1578
- case "setCompositionProperties":
1579
- logToPanel("Calling setCompositionProperties function...");
1580
- result = setCompositionProperties(args);
1581
- logToPanel("Returned from setCompositionProperties.");
1582
- break;
1583
- case "duplicateLayer":
1584
- logToPanel("Calling duplicateLayer function...");
1585
- result = duplicateLayer(args);
1586
- logToPanel("Returned from duplicateLayer.");
1587
- break;
1588
- case "deleteLayer":
1589
- logToPanel("Calling deleteLayer function...");
1590
- result = deleteLayer(args);
1591
- logToPanel("Returned from deleteLayer.");
1592
- break;
1593
- case "setLayerMask":
1594
- logToPanel("Calling setLayerMask function...");
1595
- result = setLayerMask(args);
1596
- logToPanel("Returned from setLayerMask.");
1597
- break;
1598
- default:
1599
- result = JSON.stringify({ error: "Unknown command: " + command });
1600
- }
1601
- logToPanel("Execution finished for: " + command); // Log after switch
1602
-
1603
- // Save the result (ensure result is always a string)
1604
- logToPanel("Preparing to write result file...");
1605
- var resultString = (typeof result === 'string') ? result : JSON.stringify(result);
1606
-
1607
- // Try to parse the result as JSON to add a timestamp
1608
- try {
1609
- var resultObj = JSON.parse(resultString);
1610
- // Add a timestamp to help identify if we're getting fresh results
1611
- resultObj._responseTimestamp = new Date().toISOString();
1612
- resultObj._commandExecuted = command;
1613
- resultString = JSON.stringify(resultObj, null, 2);
1614
- logToPanel("Added timestamp to result JSON for tracking freshness.");
1615
- } catch (parseError) {
1616
- // If it's not valid JSON, append the timestamp as a comment
1617
- logToPanel("Could not parse result as JSON to add timestamp: " + parseError.toString());
1618
- // We'll still continue with the original string
1619
- }
1620
-
1621
- var resultFile = new File(getResultFilePath());
1622
- resultFile.encoding = "UTF-8"; // Ensure UTF-8 encoding
1623
- logToPanel("Opening result file for writing...");
1624
- var opened = resultFile.open("w");
1625
- if (!opened) {
1626
- logToPanel("ERROR: Failed to open result file for writing: " + resultFile.fsName);
1627
- throw new Error("Failed to open result file for writing.");
1628
- }
1629
- logToPanel("Writing to result file...");
1630
- var written = resultFile.write(resultString);
1631
- if (!written) {
1632
- logToPanel("ERROR: Failed to write to result file (write returned false): " + resultFile.fsName);
1633
- // Still try to close, but log the error
1634
- }
1635
- logToPanel("Closing result file...");
1636
- var closed = resultFile.close();
1637
- if (!closed) {
1638
- logToPanel("ERROR: Failed to close result file: " + resultFile.fsName);
1639
- // Continue, but log the error
1640
- }
1641
- logToPanel("Result file write process complete.");
1642
-
1643
- logToPanel("Command completed successfully: " + command); // Changed log message
1644
- statusText.text = "Command completed: " + command;
1645
-
1646
- // Update command file status
1647
- logToPanel("Updating command status to completed...");
1648
- updateCommandStatus("completed");
1649
- logToPanel("Command status updated.");
1650
-
1651
- } catch (error) {
1652
- var errorMsg = "ERROR in executeCommand for '" + command + "': " + error.toString() + (error.line ? " (line: " + error.line + ")" : "");
1653
- logToPanel(errorMsg); // Log detailed error
1654
- statusText.text = "Error: " + error.toString();
1655
-
1656
- // Write detailed error to result file
1657
- try {
1658
- logToPanel("Attempting to write ERROR to result file...");
1659
- var errorResult = JSON.stringify({
1660
- status: "error",
1661
- command: command,
1662
- message: error.toString(),
1663
- line: error.line,
1664
- fileName: error.fileName
1665
- });
1666
- var errorFile = new File(getResultFilePath());
1667
- errorFile.encoding = "UTF-8";
1668
- if (errorFile.open("w")) {
1669
- errorFile.write(errorResult);
1670
- errorFile.close();
1671
- logToPanel("Successfully wrote ERROR to result file.");
1672
- } else {
1673
- logToPanel("CRITICAL ERROR: Failed to open result file to write error!");
1674
- }
1675
- } catch (writeError) {
1676
- logToPanel("CRITICAL ERROR: Failed to write error to result file: " + writeError.toString());
1677
- }
1678
-
1679
- // Update command file status even after error
1680
- logToPanel("Updating command status to error...");
1681
- updateCommandStatus("error");
1682
- logToPanel("Command status updated to error.");
1683
- }
1684
- }
1685
-
1686
- // Update command file status
1687
- function updateCommandStatus(status) {
1688
- try {
1689
- var commandFile = new File(getCommandFilePath());
1690
- if (commandFile.exists) {
1691
- commandFile.open("r");
1692
- var content = commandFile.read();
1693
- commandFile.close();
1694
-
1695
- if (content) {
1696
- var commandData = JSON.parse(content);
1697
- commandData.status = status;
1698
-
1699
- commandFile.open("w");
1700
- commandFile.write(JSON.stringify(commandData, null, 2));
1701
- commandFile.close();
1702
- }
1703
- }
1704
- } catch (e) {
1705
- logToPanel("Error updating command status: " + e.toString());
1706
- }
1707
- }
1708
-
1709
- // Log message to panel
1710
- function logToPanel(message) {
1711
- var timestamp = new Date().toLocaleTimeString();
1712
- logText.text = timestamp + ": " + message + "\n" + logText.text;
1713
- }
1714
-
1715
- // Check for new commands
1716
- function checkForCommands() {
1717
- if (!autoRunCheckbox.value || isChecking) return;
1718
-
1719
- isChecking = true;
1720
-
1721
- try {
1722
- var commandFile = new File(getCommandFilePath());
1723
- if (commandFile.exists) {
1724
- commandFile.open("r");
1725
- var content = commandFile.read();
1726
- commandFile.close();
1727
-
1728
- if (content) {
1729
- var commandData = (typeof JSON !== "undefined" && JSON.parse)
1730
- ? JSON.parse(content)
1731
- : eval("(" + content + ")");
1732
-
1733
- // Only execute pending commands
1734
- if (commandData.status === "pending") {
1735
- // Update status to running
1736
- updateCommandStatus("running");
1737
-
1738
- // Execute the command
1739
- executeCommand(commandData.command, commandData.args || {});
1740
- }
1741
- }
1742
- }
1743
- } catch (e) {
1744
- logToPanel("Error checking for commands: " + e.toString());
1745
- }
1746
-
1747
- isChecking = false;
1748
- }
1749
-
1750
- // Set up timer to check for commands
1751
- function startCommandChecker() {
1752
- app.scheduleTask("checkForCommands()", checkInterval, true);
1753
- }
1754
-
1755
- // Add manual check button
1756
- var checkButton = panel.add("button", undefined, "Check for Commands Now");
1757
- checkButton.onClick = function() {
1758
- logToPanel("Manually checking for commands");
1759
- checkForCommands();
1760
- };
1761
-
1762
- // Log startup
1763
- logToPanel("MCP Bridge Auto started");
1764
- logToPanel("Command file: " + getCommandFilePath());
1765
- statusText.text = "Ready - Auto-run is " + (autoRunCheckbox.value ? "ON" : "OFF");
1766
-
1767
- // Start the command checker
1768
- startCommandChecker();
1769
-
1770
- // Show the panel
1771
- panel.center();
1772
- panel.show();
1773
-