@pinepaper.studio/mcp-server 1.4.3 → 1.5.1
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.
- package/README.md +89 -0
- package/dist/__tests__/fixtures/mock-generators.d.ts +147 -0
- package/dist/__tests__/fixtures/mock-generators.d.ts.map +1 -1
- package/dist/__tests__/fixtures/mock-generators.js +85 -0
- package/dist/__tests__/fixtures/mock-generators.js.map +1 -1
- package/dist/agent/export-manager.d.ts +77 -0
- package/dist/agent/export-manager.d.ts.map +1 -0
- package/dist/agent/export-manager.js +465 -0
- package/dist/agent/export-manager.js.map +1 -0
- package/dist/agent/index.d.ts +9 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +12 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/session-manager.d.ts +109 -0
- package/dist/agent/session-manager.d.ts.map +1 -0
- package/dist/agent/session-manager.js +311 -0
- package/dist/agent/session-manager.js.map +1 -0
- package/dist/agent/types.d.ts +385 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +141 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/puppeteer-controller.d.ts +89 -5
- package/dist/browser/puppeteer-controller.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.js +412 -11
- package/dist/browser/puppeteer-controller.js.map +1 -1
- package/dist/cli.js +61 -65
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2769 -150
- package/dist/index.js.map +1 -1
- package/dist/metrics/types.d.ts +3 -1
- package/dist/metrics/types.d.ts.map +1 -1
- package/dist/prompts/index.d.ts +30 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +163 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/tools/compact-descriptions.d.ts +9 -0
- package/dist/tools/compact-descriptions.d.ts.map +1 -0
- package/dist/tools/compact-descriptions.js +291 -0
- package/dist/tools/compact-descriptions.js.map +1 -0
- package/dist/tools/definitions.d.ts +14 -0
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +4954 -1552
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +1302 -19
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts +9 -0
- package/dist/tools/minimal-descriptions.d.ts.map +1 -0
- package/dist/tools/minimal-descriptions.js +158 -0
- package/dist/tools/minimal-descriptions.js.map +1 -0
- package/dist/tools/toolkits.d.ts +51 -0
- package/dist/tools/toolkits.d.ts.map +1 -0
- package/dist/tools/toolkits.js +336 -0
- package/dist/tools/toolkits.js.map +1 -0
- package/dist/types/code-generator.d.ts +193 -1
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +2167 -26
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +5278 -1141
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +863 -5
- package/dist/types/schemas.js.map +1 -1
- package/manifest.json +94 -2
- package/package.json +1 -1
|
@@ -4,9 +4,26 @@
|
|
|
4
4
|
* Converts MCP tool inputs to executable PinePaper JavaScript code.
|
|
5
5
|
* This code is designed to run in the browser context where window.PinePaper is available.
|
|
6
6
|
*/
|
|
7
|
+
import { generateP5DrawCode } from '../tools/p5-compat/p5-helpers.js';
|
|
7
8
|
import { CreateItemInputSchema, ModifyItemInputSchema, AddRelationInputSchema, AnimateItemInputSchema, KeyframeAnimateInputSchema, ExecuteGeneratorInputSchema, ApplyEffectInputSchema, SetBackgroundColorInputSchema, SetCanvasSizeInputSchema, CreateGlossySphereInputSchema, CreateDiagonalStripesInputSchema, BatchCreateInputSchema, BatchModifyInputSchema, CreateGridInputSchema, CreateSceneInputSchema,
|
|
8
9
|
// Diagram types
|
|
9
|
-
CreateDiagramShapeInputSchema, ConnectInputSchema, ConnectPortsInputSchema, AddPortsInputSchema, AutoLayoutInputSchema, GetDiagramShapesInputSchema, UpdateConnectorInputSchema, RemoveConnectorInputSchema, DiagramModeInputSchema,
|
|
10
|
+
CreateDiagramShapeInputSchema, ConnectInputSchema, ConnectPortsInputSchema, AddPortsInputSchema, AutoLayoutInputSchema, GetDiagramShapesInputSchema, UpdateConnectorInputSchema, RemoveConnectorInputSchema, DiagramModeInputSchema,
|
|
11
|
+
// Agent flow mode types
|
|
12
|
+
AgentStartJobInputSchema, AgentEndJobInputSchema, AgentResetInputSchema, AgentBatchExecuteInputSchema, AgentExportInputSchema, AgentAnalyzeInputSchema,
|
|
13
|
+
// Interactive/Trigger types
|
|
14
|
+
AddTriggerInputSchema, RemoveTriggerInputSchema, QueryTriggersInputSchema,
|
|
15
|
+
// Quiz types
|
|
16
|
+
CreateQuizInputSchema, GetQuizStateInputSchema, ResetQuizInputSchema,
|
|
17
|
+
// Letter collage types
|
|
18
|
+
CreateLetterCollageInputSchema, AnimateLetterCollageInputSchema,
|
|
19
|
+
// Map types
|
|
20
|
+
LoadMapInputSchema, HighlightRegionsInputSchema, UnhighlightRegionsInputSchema, ApplyDataColorsInputSchema, AddMarkerInputSchema, AddMapLabelsInputSchema, PanMapInputSchema, ZoomMapInputSchema, ImportCustomMapInputSchema, GetRegionAtPointInputSchema,
|
|
21
|
+
// Map animation/CSV types
|
|
22
|
+
AnimateMapRegionsInputSchema, AnimateMapWaveInputSchema, StopMapAnimationsInputSchema, ExportMapRegionCSVInputSchema, ImportMapRegionCSVInputSchema, SelectMapRegionsInputSchema, DeselectMapRegionsInputSchema,
|
|
23
|
+
// Custom relation/code types
|
|
24
|
+
RegisterCustomRelationInputSchema, ExecuteCustomCodeInputSchema,
|
|
25
|
+
// Mask types
|
|
26
|
+
ApplyAnimatedMaskInputSchema, ApplyCustomMaskInputSchema, RemoveMaskInputSchema, } from './schemas.js';
|
|
10
27
|
// =============================================================================
|
|
11
28
|
// HELPER FUNCTIONS
|
|
12
29
|
// =============================================================================
|
|
@@ -108,6 +125,8 @@ const item = app.create('${itemType}', ${JSON.stringify(params, null, 2)});`;
|
|
|
108
125
|
if (opacity !== undefined) {
|
|
109
126
|
code += `\nitem.opacity = ${opacity};`;
|
|
110
127
|
}
|
|
128
|
+
// Ensure item is visible above backgrounds/generators
|
|
129
|
+
code += `\nif (item.bringToFront) item.bringToFront();`;
|
|
111
130
|
code += `
|
|
112
131
|
const itemId = item.data.registryId;
|
|
113
132
|
app.historyManager.saveState();
|
|
@@ -122,11 +141,8 @@ app.historyManager.saveState();
|
|
|
122
141
|
function generateModifyItemCode(itemId, properties) {
|
|
123
142
|
return `
|
|
124
143
|
// Modify item ${itemId}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
throw new Error('Item not found: ${itemId}');
|
|
128
|
-
}
|
|
129
|
-
app.modify(item, ${JSON.stringify(properties, null, 2)});
|
|
144
|
+
app.select('${itemId}');
|
|
145
|
+
app.modify(${JSON.stringify(properties, null, 2)});
|
|
130
146
|
app.historyManager.saveState();
|
|
131
147
|
|
|
132
148
|
// Return success
|
|
@@ -246,18 +262,7 @@ function generateKeyframeAnimateCode(itemId, keyframes, duration, loop = false)
|
|
|
246
262
|
const calculatedDuration = duration || Math.max(...keyframes.map(k => k.time));
|
|
247
263
|
return `
|
|
248
264
|
// Apply keyframe animation to ${itemId}
|
|
249
|
-
|
|
250
|
-
if (!item) {
|
|
251
|
-
throw new Error('Item not found: ${itemId}');
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
app.modify(item, {
|
|
255
|
-
animationType: 'keyframe',
|
|
256
|
-
keyframes: ${keyframesJson}
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// Start playback
|
|
260
|
-
app.playKeyframeTimeline(${calculatedDuration}, ${loop});
|
|
265
|
+
app.addAnimation('${itemId}', ${keyframesJson}, { duration: ${calculatedDuration}, loop: ${loop} });
|
|
261
266
|
|
|
262
267
|
({ success: true, itemId: '${itemId}', duration: ${calculatedDuration}, loop: ${loop} });
|
|
263
268
|
`.trim();
|
|
@@ -268,9 +273,10 @@ app.playKeyframeTimeline(${calculatedDuration}, ${loop});
|
|
|
268
273
|
function generateExecuteGeneratorCode(generatorName, params) {
|
|
269
274
|
return `
|
|
270
275
|
// Execute ${generatorName} generator
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
276
|
+
(async function() {
|
|
277
|
+
await app.executeGenerator('${generatorName}', ${JSON.stringify(params, null, 2)});
|
|
278
|
+
return { success: true, generator: '${generatorName}' };
|
|
279
|
+
})();
|
|
274
280
|
`.trim();
|
|
275
281
|
}
|
|
276
282
|
/**
|
|
@@ -374,9 +380,12 @@ app.setBackgroundColor('${color}');
|
|
|
374
380
|
* Template for canvas size
|
|
375
381
|
*/
|
|
376
382
|
function generateSetCanvasSizeCode(width, height, preset) {
|
|
383
|
+
const sizeArg = preset
|
|
384
|
+
? `'${preset}'`
|
|
385
|
+
: `{ width: ${width}, height: ${height} }`;
|
|
377
386
|
return `
|
|
378
387
|
// Set canvas size
|
|
379
|
-
app.setCanvasSize(${
|
|
388
|
+
app.setCanvasSize(${sizeArg});
|
|
380
389
|
app.historyManager.saveState();
|
|
381
390
|
({ success: true, width: ${width}, height: ${height} });
|
|
382
391
|
`.trim();
|
|
@@ -587,7 +596,7 @@ export class PinePaperCodeGenerator {
|
|
|
587
596
|
*/
|
|
588
597
|
generateAddRelation(input) {
|
|
589
598
|
const validated = AddRelationInputSchema.parse(input);
|
|
590
|
-
return generateAddRelationCode(validated.sourceId, validated.targetId, validated.relationType, validated.params);
|
|
599
|
+
return generateAddRelationCode(validated.sourceId, validated.targetId || validated.sourceId, validated.relationType, validated.params);
|
|
591
600
|
}
|
|
592
601
|
/**
|
|
593
602
|
* Generate code for removing a relation
|
|
@@ -915,8 +924,6 @@ app.historyManager.saveState();
|
|
|
915
924
|
* Generate code for executing p5.js-style drawing code
|
|
916
925
|
*/
|
|
917
926
|
generateP5Draw(code) {
|
|
918
|
-
// Import the p5 helpers generator
|
|
919
|
-
const { generateP5DrawCode } = require('../tools/p5-compat/p5-helpers.js');
|
|
920
927
|
return generateP5DrawCode(code);
|
|
921
928
|
}
|
|
922
929
|
/**
|
|
@@ -1033,6 +1040,7 @@ if (typeof app.generators !== 'undefined' && app.generators['${backgroundGenerat
|
|
|
1033
1040
|
}
|
|
1034
1041
|
|
|
1035
1042
|
const itemId = app.registerItem(item, '${name}', { source: 'mcp-scene' });
|
|
1043
|
+
if (item.bringToFront) item.bringToFront();
|
|
1036
1044
|
nameToId['${name}'] = itemId;
|
|
1037
1045
|
results.items.push({ name: '${name}', itemId, type: '${itemType}' });
|
|
1038
1046
|
})();
|
|
@@ -1071,7 +1079,7 @@ if (typeof app.generators !== 'undefined' && app.generators['${backgroundGenerat
|
|
|
1071
1079
|
const targetItem = app.itemRegistry.get(targetId);
|
|
1072
1080
|
if (targetItem) {
|
|
1073
1081
|
const params = ${paramsStr};
|
|
1074
|
-
app.
|
|
1082
|
+
app.animate(targetItem, { animationType: '${type}', ...params });
|
|
1075
1083
|
results.animations.push({ target: '${target}', type: '${type}' });
|
|
1076
1084
|
}
|
|
1077
1085
|
}
|
|
@@ -1324,6 +1332,2139 @@ throw new Error('Unknown diagram mode action: ${action}');
|
|
|
1324
1332
|
`.trim();
|
|
1325
1333
|
}
|
|
1326
1334
|
}
|
|
1335
|
+
// ===========================================================================
|
|
1336
|
+
// AGENT FLOW MODE CODE GENERATORS
|
|
1337
|
+
// ===========================================================================
|
|
1338
|
+
/**
|
|
1339
|
+
* Generate code for starting an agent job
|
|
1340
|
+
*/
|
|
1341
|
+
generateAgentStartJob(input) {
|
|
1342
|
+
const validated = AgentStartJobInputSchema.parse(input);
|
|
1343
|
+
const { name, screenshotPolicy, canvasPreset, clearCanvas } = validated;
|
|
1344
|
+
const nameStr = name ? `'${name.replace(/'/g, "\\'")}'` : 'null';
|
|
1345
|
+
const policyStr = screenshotPolicy || 'on_complete';
|
|
1346
|
+
const shouldClear = clearCanvas !== false;
|
|
1347
|
+
let code = `
|
|
1348
|
+
// Start agent job
|
|
1349
|
+
(async function() {
|
|
1350
|
+
const jobOptions = {
|
|
1351
|
+
name: ${nameStr},
|
|
1352
|
+
screenshotPolicy: '${policyStr}',
|
|
1353
|
+
agentMode: true
|
|
1354
|
+
};
|
|
1355
|
+
`;
|
|
1356
|
+
if (shouldClear) {
|
|
1357
|
+
code += `
|
|
1358
|
+
// Clear canvas
|
|
1359
|
+
if (app.clearCanvas) {
|
|
1360
|
+
app.clearCanvas();
|
|
1361
|
+
} else {
|
|
1362
|
+
if (app.textItemGroup) app.textItemGroup.removeChildren();
|
|
1363
|
+
if (app.patternGroup) app.patternGroup.removeChildren();
|
|
1364
|
+
if (app.itemRegistry) app.itemRegistry.clear();
|
|
1365
|
+
if (app.relationRegistry) app.relationRegistry.clear();
|
|
1366
|
+
}
|
|
1367
|
+
`;
|
|
1368
|
+
}
|
|
1369
|
+
if (canvasPreset) {
|
|
1370
|
+
code += `
|
|
1371
|
+
// Set canvas size to preset
|
|
1372
|
+
app.setCanvasSize('${canvasPreset}');
|
|
1373
|
+
// Allow canvas resize to take effect
|
|
1374
|
+
await new Promise(r => setTimeout(r, 50));
|
|
1375
|
+
`;
|
|
1376
|
+
}
|
|
1377
|
+
code += `
|
|
1378
|
+
// Return job context with canvas size
|
|
1379
|
+
const _cs = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 };
|
|
1380
|
+
return {
|
|
1381
|
+
success: true,
|
|
1382
|
+
jobStarted: true,
|
|
1383
|
+
name: ${nameStr},
|
|
1384
|
+
screenshotPolicy: '${policyStr}',
|
|
1385
|
+
canvasPreset: ${canvasPreset ? `'${canvasPreset}'` : 'null'},
|
|
1386
|
+
canvasCleared: ${shouldClear},
|
|
1387
|
+
canvasSize: { width: _cs.width || 800, height: _cs.height || 600 }
|
|
1388
|
+
};
|
|
1389
|
+
})();
|
|
1390
|
+
`;
|
|
1391
|
+
return code.trim();
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Generate code for ending an agent job
|
|
1395
|
+
*/
|
|
1396
|
+
generateAgentEndJob(input) {
|
|
1397
|
+
const validated = AgentEndJobInputSchema.parse(input);
|
|
1398
|
+
const { takeScreenshot, analyzeContent } = validated;
|
|
1399
|
+
const shouldAnalyze = analyzeContent !== false;
|
|
1400
|
+
let code = `
|
|
1401
|
+
// End agent job
|
|
1402
|
+
(async function() {
|
|
1403
|
+
const result = {
|
|
1404
|
+
success: true,
|
|
1405
|
+
jobEnded: true
|
|
1406
|
+
};
|
|
1407
|
+
`;
|
|
1408
|
+
if (shouldAnalyze) {
|
|
1409
|
+
code += `
|
|
1410
|
+
// Analyze content
|
|
1411
|
+
const analysis = {
|
|
1412
|
+
hasAnimations: false,
|
|
1413
|
+
animationTypes: [],
|
|
1414
|
+
colorComplexity: 'simple',
|
|
1415
|
+
itemCount: 0,
|
|
1416
|
+
canvasSize: { width: 0, height: 0 },
|
|
1417
|
+
hasRelations: false,
|
|
1418
|
+
relationTypes: [],
|
|
1419
|
+
hasGradients: false,
|
|
1420
|
+
hasShadows: false,
|
|
1421
|
+
hasText: false,
|
|
1422
|
+
hasImages: false,
|
|
1423
|
+
};
|
|
1424
|
+
|
|
1425
|
+
// Get canvas size
|
|
1426
|
+
if (app.canvasEl) {
|
|
1427
|
+
analysis.canvasSize = { width: app.canvasEl.width, height: app.canvasEl.height };
|
|
1428
|
+
} else if (paper.view) {
|
|
1429
|
+
analysis.canvasSize = { width: paper.view.size.width, height: paper.view.size.height };
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
// Count items and analyze
|
|
1433
|
+
const items = app.itemRegistry ? app.itemRegistry.getAll() : [];
|
|
1434
|
+
analysis.itemCount = items.length;
|
|
1435
|
+
|
|
1436
|
+
const animationSet = new Set();
|
|
1437
|
+
const relationSet = new Set();
|
|
1438
|
+
|
|
1439
|
+
items.forEach(entry => {
|
|
1440
|
+
const item = entry.item;
|
|
1441
|
+
const data = item.data || {};
|
|
1442
|
+
|
|
1443
|
+
if (entry.type === 'text' || item.className === 'PointText') analysis.hasText = true;
|
|
1444
|
+
if (item.className === 'Raster') analysis.hasImages = true;
|
|
1445
|
+
if (data.animationType) {
|
|
1446
|
+
analysis.hasAnimations = true;
|
|
1447
|
+
animationSet.add(data.animationType);
|
|
1448
|
+
}
|
|
1449
|
+
// Detect keyframe animations (added via app.addAnimation with keyframes array)
|
|
1450
|
+
if (data.keyframes || data.animation || data.keyframeAnimation) {
|
|
1451
|
+
analysis.hasAnimations = true;
|
|
1452
|
+
animationSet.add('keyframe');
|
|
1453
|
+
}
|
|
1454
|
+
if (item.fillColor && item.fillColor.gradient) analysis.hasGradients = true;
|
|
1455
|
+
if (item.shadowColor || item.shadowBlur) analysis.hasShadows = true;
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
// Check animation manager for registered animations
|
|
1459
|
+
if (app.animationManager) {
|
|
1460
|
+
const animations = app.animationManager.getAll ? app.animationManager.getAll() : (app.animationManager.animations || []);
|
|
1461
|
+
if (animations.length > 0) {
|
|
1462
|
+
analysis.hasAnimations = true;
|
|
1463
|
+
animationSet.add('managed');
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
// Check timeline for active animations
|
|
1467
|
+
if (app.timeline && (app.timeline.isPlaying || app.timeline.animations?.length > 0)) {
|
|
1468
|
+
analysis.hasAnimations = true;
|
|
1469
|
+
animationSet.add('timeline');
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
analysis.animationTypes = Array.from(animationSet);
|
|
1473
|
+
|
|
1474
|
+
// Check relations
|
|
1475
|
+
if (app.relationRegistry) {
|
|
1476
|
+
const relations = app.relationRegistry.getAll ? app.relationRegistry.getAll() : [];
|
|
1477
|
+
analysis.hasRelations = relations.length > 0;
|
|
1478
|
+
relations.forEach(rel => {
|
|
1479
|
+
if (rel.relationType) relationSet.add(rel.relationType);
|
|
1480
|
+
});
|
|
1481
|
+
analysis.relationTypes = Array.from(relationSet);
|
|
1482
|
+
if (analysis.hasRelations) analysis.hasAnimations = true;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// Determine color complexity
|
|
1486
|
+
if (analysis.hasGradients) {
|
|
1487
|
+
analysis.colorComplexity = 'gradient';
|
|
1488
|
+
} else if (analysis.itemCount > 20 || analysis.hasShadows) {
|
|
1489
|
+
analysis.colorComplexity = 'complex';
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
result.analysis = analysis;
|
|
1493
|
+
|
|
1494
|
+
// Generate recommendations
|
|
1495
|
+
const recommendations = [];
|
|
1496
|
+
if (analysis.hasAnimations) {
|
|
1497
|
+
if (analysis.hasGradients || analysis.colorComplexity === 'complex') {
|
|
1498
|
+
recommendations.push({ platform: 'web', format: 'webm', confidence: 0.9, reason: 'Animated content with gradients' });
|
|
1499
|
+
recommendations.push({ platform: 'instagram', format: 'mp4', confidence: 0.85, reason: 'Social media video' });
|
|
1500
|
+
} else {
|
|
1501
|
+
recommendations.push({ platform: 'web', format: 'svg', confidence: 0.95, reason: 'Simple animations as SVG' });
|
|
1502
|
+
recommendations.push({ platform: 'twitter', format: 'gif', confidence: 0.8, reason: 'Social media GIF' });
|
|
1503
|
+
}
|
|
1504
|
+
} else {
|
|
1505
|
+
if (analysis.colorComplexity === 'simple' && !analysis.hasImages) {
|
|
1506
|
+
recommendations.push({ platform: 'web', format: 'svg', confidence: 0.95, reason: 'Vector graphics' });
|
|
1507
|
+
} else {
|
|
1508
|
+
recommendations.push({ platform: 'instagram', format: 'png', confidence: 0.9, reason: 'High quality static' });
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
result.recommendations = recommendations;
|
|
1512
|
+
`;
|
|
1513
|
+
}
|
|
1514
|
+
if (takeScreenshot) {
|
|
1515
|
+
code += `
|
|
1516
|
+
// Capture screenshot
|
|
1517
|
+
const canvas = document.querySelector('canvas');
|
|
1518
|
+
if (canvas) {
|
|
1519
|
+
result.screenshot = canvas.toDataURL('image/png');
|
|
1520
|
+
}
|
|
1521
|
+
`;
|
|
1522
|
+
}
|
|
1523
|
+
code += `
|
|
1524
|
+
return result;
|
|
1525
|
+
})();
|
|
1526
|
+
`;
|
|
1527
|
+
return code.trim();
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Generate code for fast canvas reset
|
|
1531
|
+
*/
|
|
1532
|
+
generateAgentReset(input) {
|
|
1533
|
+
const validated = AgentResetInputSchema.parse(input);
|
|
1534
|
+
const { canvasPreset, backgroundColor, preserveBackground } = validated;
|
|
1535
|
+
let code = `
|
|
1536
|
+
// Fast canvas reset
|
|
1537
|
+
(async function() {
|
|
1538
|
+
`;
|
|
1539
|
+
if (!preserveBackground) {
|
|
1540
|
+
code += `
|
|
1541
|
+
// Clear canvas completely
|
|
1542
|
+
if (app.clearCanvas) {
|
|
1543
|
+
app.clearCanvas();
|
|
1544
|
+
} else {
|
|
1545
|
+
if (app.textItemGroup) app.textItemGroup.removeChildren();
|
|
1546
|
+
if (app.patternGroup) app.patternGroup.removeChildren();
|
|
1547
|
+
if (app.itemRegistry) app.itemRegistry.clear();
|
|
1548
|
+
if (app.relationRegistry) app.relationRegistry.clear();
|
|
1549
|
+
}
|
|
1550
|
+
`;
|
|
1551
|
+
}
|
|
1552
|
+
else {
|
|
1553
|
+
code += `
|
|
1554
|
+
// Clear items but preserve background
|
|
1555
|
+
if (app.textItemGroup) app.textItemGroup.removeChildren();
|
|
1556
|
+
if (app.itemRegistry) app.itemRegistry.clear();
|
|
1557
|
+
if (app.relationRegistry) app.relationRegistry.clear();
|
|
1558
|
+
`;
|
|
1559
|
+
}
|
|
1560
|
+
if (canvasPreset) {
|
|
1561
|
+
code += `
|
|
1562
|
+
// Set canvas to preset
|
|
1563
|
+
app.setCanvasSize('${canvasPreset}');
|
|
1564
|
+
// Allow canvas resize to take effect
|
|
1565
|
+
await new Promise(r => setTimeout(r, 50));
|
|
1566
|
+
`;
|
|
1567
|
+
}
|
|
1568
|
+
if (backgroundColor && !preserveBackground) {
|
|
1569
|
+
code += `
|
|
1570
|
+
// Set background color
|
|
1571
|
+
app.setBackgroundColor('${backgroundColor}');
|
|
1572
|
+
`;
|
|
1573
|
+
}
|
|
1574
|
+
code += `
|
|
1575
|
+
// Save state
|
|
1576
|
+
if (app.historyManager) app.historyManager.saveState();
|
|
1577
|
+
const _cs = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 };
|
|
1578
|
+
return { success: true, reset: true, canvasPreset: ${canvasPreset ? `'${canvasPreset}'` : 'null'}, canvasSize: { width: _cs.width || 800, height: _cs.height || 600 } };
|
|
1579
|
+
})();
|
|
1580
|
+
`;
|
|
1581
|
+
return code.trim();
|
|
1582
|
+
}
|
|
1583
|
+
/**
|
|
1584
|
+
* Generate code for batch execute
|
|
1585
|
+
*/
|
|
1586
|
+
generateAgentBatchExecute(input) {
|
|
1587
|
+
const validated = AgentBatchExecuteInputSchema.parse(input);
|
|
1588
|
+
const { operations, atomic } = validated;
|
|
1589
|
+
const isAtomic = atomic !== false;
|
|
1590
|
+
let code = `
|
|
1591
|
+
// Batch execute ${operations.length} operations
|
|
1592
|
+
(async function() {
|
|
1593
|
+
const results = [];
|
|
1594
|
+
const itemIds = [];
|
|
1595
|
+
let success = true;
|
|
1596
|
+
|
|
1597
|
+
try {
|
|
1598
|
+
`;
|
|
1599
|
+
operations.forEach((op, index) => {
|
|
1600
|
+
const opCode = this.generateBatchOperationCode(op, index);
|
|
1601
|
+
code += `
|
|
1602
|
+
// Operation ${index}: ${op.type}
|
|
1603
|
+
try {
|
|
1604
|
+
const result${index} = await (async () => {
|
|
1605
|
+
${opCode}
|
|
1606
|
+
})();
|
|
1607
|
+
results.push({ index: ${index}, success: true, result: result${index} });
|
|
1608
|
+
if (result${index} && result${index}.itemId) {
|
|
1609
|
+
itemIds.push(result${index}.itemId);
|
|
1610
|
+
}
|
|
1611
|
+
} catch (opError) {
|
|
1612
|
+
results.push({ index: ${index}, success: false, error: opError.message });
|
|
1613
|
+
${isAtomic ? 'throw opError;' : 'success = false;'}
|
|
1614
|
+
}
|
|
1615
|
+
`;
|
|
1616
|
+
});
|
|
1617
|
+
code += `
|
|
1618
|
+
// Save state
|
|
1619
|
+
if (app.historyManager) app.historyManager.saveState();
|
|
1620
|
+
|
|
1621
|
+
const _cs = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 };
|
|
1622
|
+
return { success, itemIds, results, operationCount: ${operations.length}, canvasSize: { width: _cs.width || 800, height: _cs.height || 600 } };
|
|
1623
|
+
} catch (e) {
|
|
1624
|
+
const _cs = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 };
|
|
1625
|
+
return { success: false, error: e.message, itemIds, results, operationCount: ${operations.length}, canvasSize: { width: _cs.width || 800, height: _cs.height || 600 } };
|
|
1626
|
+
}
|
|
1627
|
+
})();
|
|
1628
|
+
`;
|
|
1629
|
+
return code.trim();
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Generate code for a single batch operation
|
|
1633
|
+
*/
|
|
1634
|
+
generateBatchOperationCode(op, index) {
|
|
1635
|
+
switch (op.type) {
|
|
1636
|
+
case 'create':
|
|
1637
|
+
const pos = op.position || { x: 400, y: 300 };
|
|
1638
|
+
const props = JSON.stringify(op.properties || {});
|
|
1639
|
+
return `
|
|
1640
|
+
const item = app.create('${op.itemType}', { position: { x: ${pos.x}, y: ${pos.y} }, ...${props} });
|
|
1641
|
+
const itemId = item.data && item.data.id ? item.data.id : app.registerItem(item, '${op.itemType}', { source: 'mcp-batch' });
|
|
1642
|
+
// Ensure item is visible above backgrounds/generators
|
|
1643
|
+
if (item.bringToFront) item.bringToFront();
|
|
1644
|
+
return { itemId };
|
|
1645
|
+
`;
|
|
1646
|
+
case 'modify':
|
|
1647
|
+
const itemRef = op.itemId?.startsWith('$')
|
|
1648
|
+
? `itemIds[${op.itemId.substring(1)}]`
|
|
1649
|
+
: `'${op.itemId}'`;
|
|
1650
|
+
const modifyProps = JSON.stringify(op.properties || {});
|
|
1651
|
+
return `
|
|
1652
|
+
const targetId = ${itemRef};
|
|
1653
|
+
app.select(targetId);
|
|
1654
|
+
app.modify(${modifyProps});
|
|
1655
|
+
return { itemId: targetId, modified: true };
|
|
1656
|
+
`;
|
|
1657
|
+
case 'animate':
|
|
1658
|
+
const animItemRef = op.itemId?.startsWith('$')
|
|
1659
|
+
? `itemIds[${op.itemId.substring(1)}]`
|
|
1660
|
+
: `'${op.itemId}'`;
|
|
1661
|
+
const animOpts = JSON.stringify(op.animationOptions || {});
|
|
1662
|
+
return `
|
|
1663
|
+
const targetId = ${animItemRef};
|
|
1664
|
+
const item = app.getItemById(targetId);
|
|
1665
|
+
if (!item) throw new Error('Item not found: ' + targetId);
|
|
1666
|
+
app.animate(item, { animationType: '${op.animationType}', ...${animOpts} });
|
|
1667
|
+
return { itemId: targetId, animationType: '${op.animationType}' };
|
|
1668
|
+
`;
|
|
1669
|
+
case 'relation':
|
|
1670
|
+
const srcRef = op.sourceId?.startsWith('$')
|
|
1671
|
+
? `itemIds[${op.sourceId.substring(1)}]`
|
|
1672
|
+
: `'${op.sourceId}'`;
|
|
1673
|
+
const tgtRef = op.targetId?.startsWith('$')
|
|
1674
|
+
? `itemIds[${op.targetId.substring(1)}]`
|
|
1675
|
+
: `'${op.targetId}'`;
|
|
1676
|
+
const relOpts = JSON.stringify(op.relationOptions || {});
|
|
1677
|
+
return `
|
|
1678
|
+
const sourceId = ${srcRef};
|
|
1679
|
+
const targetId = ${tgtRef};
|
|
1680
|
+
const source = app.getItemById(sourceId);
|
|
1681
|
+
const target = app.getItemById(targetId);
|
|
1682
|
+
if (!source) throw new Error('Source not found: ' + sourceId);
|
|
1683
|
+
if (!target) throw new Error('Target not found: ' + targetId);
|
|
1684
|
+
const relationId = app.addRelation(source, target, '${op.relationType}', ${relOpts});
|
|
1685
|
+
return { relationId, sourceId, targetId, relationType: '${op.relationType}' };
|
|
1686
|
+
`;
|
|
1687
|
+
case 'delete':
|
|
1688
|
+
const delRef = op.itemId?.startsWith('$')
|
|
1689
|
+
? `itemIds[${op.itemId.substring(1)}]`
|
|
1690
|
+
: `'${op.itemId}'`;
|
|
1691
|
+
return `
|
|
1692
|
+
const targetId = ${delRef};
|
|
1693
|
+
const item = app.getItemById(targetId);
|
|
1694
|
+
if (!item) throw new Error('Item not found: ' + targetId);
|
|
1695
|
+
item.remove();
|
|
1696
|
+
if (app.itemRegistry) app.itemRegistry.unregister(targetId);
|
|
1697
|
+
return { itemId: targetId, deleted: true };
|
|
1698
|
+
`;
|
|
1699
|
+
case 'set_background':
|
|
1700
|
+
const bgColor = op.backgroundColor || op.properties?.color || '#000000';
|
|
1701
|
+
return `
|
|
1702
|
+
app.setBackgroundColor('${bgColor}');
|
|
1703
|
+
return { success: true, backgroundColor: '${bgColor}' };
|
|
1704
|
+
`;
|
|
1705
|
+
case 'execute_generator':
|
|
1706
|
+
const genName = op.generatorName || 'drawSunburst';
|
|
1707
|
+
const genParams = JSON.stringify(op.generatorParams || {});
|
|
1708
|
+
return `
|
|
1709
|
+
await app.executeGenerator('${genName}', ${genParams});
|
|
1710
|
+
return { success: true, generator: '${genName}' };
|
|
1711
|
+
`;
|
|
1712
|
+
case 'set_canvas_size': {
|
|
1713
|
+
const w = op.width || 1080;
|
|
1714
|
+
const h = op.height || 1080;
|
|
1715
|
+
const sizeArg = op.preset ? `'${op.preset}'` : `{ width: ${w}, height: ${h} }`;
|
|
1716
|
+
return `
|
|
1717
|
+
app.setCanvasSize(${sizeArg});
|
|
1718
|
+
await new Promise(r => setTimeout(r, 50));
|
|
1719
|
+
return { success: true, width: ${w}, height: ${h} };
|
|
1720
|
+
`;
|
|
1721
|
+
}
|
|
1722
|
+
case 'keyframe_animate': {
|
|
1723
|
+
const kfItemRef = op.itemId?.startsWith('$')
|
|
1724
|
+
? `itemIds[${op.itemId.substring(1)}]`
|
|
1725
|
+
: `'${op.itemId}'`;
|
|
1726
|
+
const kfJson = JSON.stringify(op.keyframes || []);
|
|
1727
|
+
const kfDuration = op.duration || (op.keyframes?.length ? Math.max(...op.keyframes.map(k => k.time)) : 5);
|
|
1728
|
+
const kfLoop = op.loop ?? false;
|
|
1729
|
+
return `
|
|
1730
|
+
const targetId = ${kfItemRef};
|
|
1731
|
+
app.addAnimation(targetId, ${kfJson}, { duration: ${kfDuration}, loop: ${kfLoop} });
|
|
1732
|
+
return { itemId: targetId, duration: ${kfDuration}, loop: ${kfLoop} };
|
|
1733
|
+
`;
|
|
1734
|
+
}
|
|
1735
|
+
case 'apply_mask': {
|
|
1736
|
+
const maskItemRef = op.itemId?.startsWith('$')
|
|
1737
|
+
? `itemIds[${op.itemId.substring(1)}]`
|
|
1738
|
+
: `'${op.itemId}'`;
|
|
1739
|
+
const maskOpts = JSON.stringify(op.maskOptions || {});
|
|
1740
|
+
return `
|
|
1741
|
+
const targetId = ${maskItemRef};
|
|
1742
|
+
if (!app.maskSystem) throw new Error('Mask system not available');
|
|
1743
|
+
const result = app.maskSystem.applyAnimatedMask(targetId, {
|
|
1744
|
+
preset: ${op.maskPreset ? `'${op.maskPreset}'` : 'null'},
|
|
1745
|
+
maskType: ${op.maskType ? `'${op.maskType}'` : 'null'},
|
|
1746
|
+
options: ${maskOpts},
|
|
1747
|
+
maskOptions: ${maskOpts}
|
|
1748
|
+
});
|
|
1749
|
+
return result;
|
|
1750
|
+
`;
|
|
1751
|
+
}
|
|
1752
|
+
case 'apply_effect': {
|
|
1753
|
+
const effItemRef = op.itemId?.startsWith('$')
|
|
1754
|
+
? `itemIds[${op.itemId.substring(1)}]`
|
|
1755
|
+
: `'${op.itemId}'`;
|
|
1756
|
+
const effParams = JSON.stringify(op.effectParams || {});
|
|
1757
|
+
return `
|
|
1758
|
+
const targetId = ${effItemRef};
|
|
1759
|
+
const item = app.getItemById(targetId);
|
|
1760
|
+
if (!item) throw new Error('Item not found: ' + targetId);
|
|
1761
|
+
app.applyEffect(item, '${op.effectType || 'sparkle'}', ${effParams});
|
|
1762
|
+
return { itemId: targetId, effectType: '${op.effectType || 'sparkle'}' };
|
|
1763
|
+
`;
|
|
1764
|
+
}
|
|
1765
|
+
case 'play_timeline': {
|
|
1766
|
+
const ptAction = op.action || 'play';
|
|
1767
|
+
const ptDuration = op.duration || 5;
|
|
1768
|
+
const ptLoop = op.loop ?? false;
|
|
1769
|
+
if (ptAction === 'play') {
|
|
1770
|
+
return `
|
|
1771
|
+
app.playKeyframeTimeline(${ptDuration}, ${ptLoop});
|
|
1772
|
+
return { success: true, action: 'play', duration: ${ptDuration}, loop: ${ptLoop} };
|
|
1773
|
+
`;
|
|
1774
|
+
}
|
|
1775
|
+
else if (ptAction === 'stop') {
|
|
1776
|
+
return `
|
|
1777
|
+
app.stopKeyframeTimeline();
|
|
1778
|
+
return { success: true, action: 'stop' };
|
|
1779
|
+
`;
|
|
1780
|
+
}
|
|
1781
|
+
else {
|
|
1782
|
+
return `
|
|
1783
|
+
app.setPlaybackTime(${op.time || 0});
|
|
1784
|
+
return { success: true, action: 'seek', time: ${op.time || 0} };
|
|
1785
|
+
`;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
default:
|
|
1789
|
+
return `throw new Error('Unknown operation type: ${op.type}');`;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Generate code for smart export
|
|
1794
|
+
*/
|
|
1795
|
+
generateAgentExport(input) {
|
|
1796
|
+
const validated = AgentExportInputSchema.parse(input);
|
|
1797
|
+
const { platform, format, quality } = validated;
|
|
1798
|
+
const qualityLevel = quality || 'standard';
|
|
1799
|
+
// Quality settings
|
|
1800
|
+
const qualitySettings = {
|
|
1801
|
+
draft: { compression: 0.6, fps: 15, dpi: 72 },
|
|
1802
|
+
standard: { compression: 0.85, fps: 30, dpi: 150 },
|
|
1803
|
+
high: { compression: 0.95, fps: 60, dpi: 300 },
|
|
1804
|
+
}[qualityLevel];
|
|
1805
|
+
// Platform presets
|
|
1806
|
+
const platformPresets = {
|
|
1807
|
+
'instagram': { width: 1080, height: 1080, staticFormat: 'png', animatedFormat: 'mp4' },
|
|
1808
|
+
'instagram-story': { width: 1080, height: 1920, staticFormat: 'png', animatedFormat: 'mp4' },
|
|
1809
|
+
'tiktok': { width: 1080, height: 1920, staticFormat: 'png', animatedFormat: 'mp4' },
|
|
1810
|
+
'youtube': { width: 1920, height: 1080, staticFormat: 'png', animatedFormat: 'mp4' },
|
|
1811
|
+
'youtube-thumbnail': { width: 1280, height: 720, staticFormat: 'png', animatedFormat: 'png' },
|
|
1812
|
+
'twitter': { width: 1200, height: 675, staticFormat: 'png', animatedFormat: 'gif' },
|
|
1813
|
+
'linkedin': { width: 1200, height: 627, staticFormat: 'png', animatedFormat: 'gif' },
|
|
1814
|
+
'web': { width: 800, height: 600, staticFormat: 'svg', animatedFormat: 'svg' },
|
|
1815
|
+
'print-a4': { width: 2480, height: 3508, staticFormat: 'pdf', animatedFormat: 'pdf' },
|
|
1816
|
+
'print-letter': { width: 2550, height: 3300, staticFormat: 'pdf', animatedFormat: 'pdf' },
|
|
1817
|
+
};
|
|
1818
|
+
const preset = platformPresets[platform] || platformPresets['web'];
|
|
1819
|
+
// Resolve "auto" format to the platform's recommended format
|
|
1820
|
+
const exportFormat = (!format || format === 'auto') ? preset.staticFormat : format;
|
|
1821
|
+
return `
|
|
1822
|
+
// Smart export for ${platform}
|
|
1823
|
+
(async function() {
|
|
1824
|
+
const platform = '${platform}';
|
|
1825
|
+
const format = '${exportFormat}';
|
|
1826
|
+
const quality = '${qualityLevel}';
|
|
1827
|
+
const settings = ${JSON.stringify(qualitySettings)};
|
|
1828
|
+
const dimensions = ${JSON.stringify({ width: preset.width, height: preset.height })};
|
|
1829
|
+
|
|
1830
|
+
let result = { success: false, platform, format, quality };
|
|
1831
|
+
|
|
1832
|
+
try {
|
|
1833
|
+
switch (format) {
|
|
1834
|
+
case 'svg':
|
|
1835
|
+
const svgString = app.exportAnimatedSVG ? app.exportAnimatedSVG() : app.exportSVG();
|
|
1836
|
+
result = {
|
|
1837
|
+
success: true,
|
|
1838
|
+
platform,
|
|
1839
|
+
format: 'svg',
|
|
1840
|
+
data: svgString,
|
|
1841
|
+
mimeType: 'image/svg+xml',
|
|
1842
|
+
size: new Blob([svgString]).size,
|
|
1843
|
+
hasAnimations: svgString.includes('<animate') || svgString.includes('@keyframes')
|
|
1844
|
+
};
|
|
1845
|
+
break;
|
|
1846
|
+
|
|
1847
|
+
case 'png':
|
|
1848
|
+
if (app.exportEngine && app.exportEngine.exportPNG) {
|
|
1849
|
+
const dataUrl = await app.exportEngine.exportPNG({ dpi: settings.dpi });
|
|
1850
|
+
result = {
|
|
1851
|
+
success: true,
|
|
1852
|
+
platform,
|
|
1853
|
+
format: 'png',
|
|
1854
|
+
data: dataUrl,
|
|
1855
|
+
mimeType: 'image/png',
|
|
1856
|
+
size: Math.round(dataUrl.length * 0.75)
|
|
1857
|
+
};
|
|
1858
|
+
} else {
|
|
1859
|
+
const canvas = document.querySelector('canvas');
|
|
1860
|
+
if (canvas) {
|
|
1861
|
+
const dataUrl = canvas.toDataURL('image/png');
|
|
1862
|
+
result = {
|
|
1863
|
+
success: true,
|
|
1864
|
+
platform,
|
|
1865
|
+
format: 'png',
|
|
1866
|
+
data: dataUrl,
|
|
1867
|
+
mimeType: 'image/png',
|
|
1868
|
+
size: Math.round(dataUrl.length * 0.75)
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
break;
|
|
1873
|
+
|
|
1874
|
+
case 'gif':
|
|
1875
|
+
case 'mp4':
|
|
1876
|
+
case 'webm': {
|
|
1877
|
+
const videoMimeType = { mp4: 'video/mp4', webm: 'video/webm', gif: 'image/gif' }[format];
|
|
1878
|
+
// Use _quickExportVideo (delegates to VideoExporter.export internally)
|
|
1879
|
+
if (app.exportEngine && app.exportEngine._quickExportVideo) {
|
|
1880
|
+
const videoResult = await app.exportEngine._quickExportVideo(format, { fps: settings.fps, duration: 5 }, false);
|
|
1881
|
+
if (videoResult && videoResult.blob) {
|
|
1882
|
+
const reader = new FileReader();
|
|
1883
|
+
const dataUrl = await new Promise(resolve => {
|
|
1884
|
+
reader.onloadend = () => resolve(reader.result);
|
|
1885
|
+
reader.readAsDataURL(videoResult.blob);
|
|
1886
|
+
});
|
|
1887
|
+
result = { success: true, platform, format, data: dataUrl, mimeType: videoMimeType, size: videoResult.blob.size };
|
|
1888
|
+
} else {
|
|
1889
|
+
result = { success: false, error: format.toUpperCase() + ' export returned no data' };
|
|
1890
|
+
}
|
|
1891
|
+
} else if (app.exportEngine && app.exportEngine.videoExporter) {
|
|
1892
|
+
// Fallback: call VideoExporter.export directly
|
|
1893
|
+
const blob = await app.exportEngine.videoExporter.export({ format, fps: settings.fps, duration: 5 });
|
|
1894
|
+
const reader = new FileReader();
|
|
1895
|
+
const dataUrl = await new Promise(resolve => {
|
|
1896
|
+
reader.onloadend = () => resolve(reader.result);
|
|
1897
|
+
reader.readAsDataURL(blob);
|
|
1898
|
+
});
|
|
1899
|
+
result = { success: true, platform, format, data: dataUrl, mimeType: videoMimeType, size: blob.size };
|
|
1900
|
+
} else {
|
|
1901
|
+
result = { success: false, error: format.toUpperCase() + ' export not available' };
|
|
1902
|
+
}
|
|
1903
|
+
break;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
case 'pdf':
|
|
1907
|
+
if (app.exportEngine && app.exportEngine.exportPDF) {
|
|
1908
|
+
const blob = await app.exportEngine.exportPDF({ dpi: settings.dpi });
|
|
1909
|
+
const reader = new FileReader();
|
|
1910
|
+
const dataUrl = await new Promise(resolve => {
|
|
1911
|
+
reader.onloadend = () => resolve(reader.result);
|
|
1912
|
+
reader.readAsDataURL(blob);
|
|
1913
|
+
});
|
|
1914
|
+
result = { success: true, platform, format: 'pdf', data: dataUrl, mimeType: 'application/pdf', size: blob.size, dpi: settings.dpi };
|
|
1915
|
+
} else {
|
|
1916
|
+
result = { success: false, error: 'PDF export failed' };
|
|
1917
|
+
}
|
|
1918
|
+
break;
|
|
1919
|
+
|
|
1920
|
+
default:
|
|
1921
|
+
result = { success: false, error: 'Unknown format: ' + format };
|
|
1922
|
+
}
|
|
1923
|
+
} catch (e) {
|
|
1924
|
+
result = { success: false, error: e.message, platform, format };
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
return result;
|
|
1928
|
+
})();
|
|
1929
|
+
`.trim();
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Generate code for content analysis
|
|
1933
|
+
*/
|
|
1934
|
+
generateAgentAnalyze(input) {
|
|
1935
|
+
AgentAnalyzeInputSchema.parse(input);
|
|
1936
|
+
return `
|
|
1937
|
+
// Analyze canvas content
|
|
1938
|
+
(function() {
|
|
1939
|
+
const analysis = {
|
|
1940
|
+
hasAnimations: false,
|
|
1941
|
+
animationTypes: [],
|
|
1942
|
+
colorComplexity: 'simple',
|
|
1943
|
+
itemCount: 0,
|
|
1944
|
+
canvasSize: { width: 0, height: 0 },
|
|
1945
|
+
hasRelations: false,
|
|
1946
|
+
relationTypes: [],
|
|
1947
|
+
hasGradients: false,
|
|
1948
|
+
hasShadows: false,
|
|
1949
|
+
hasText: false,
|
|
1950
|
+
hasImages: false,
|
|
1951
|
+
};
|
|
1952
|
+
|
|
1953
|
+
// Get canvas size
|
|
1954
|
+
if (app.canvasEl) {
|
|
1955
|
+
analysis.canvasSize = { width: app.canvasEl.width, height: app.canvasEl.height };
|
|
1956
|
+
} else if (paper.view) {
|
|
1957
|
+
analysis.canvasSize = { width: paper.view.size.width, height: paper.view.size.height };
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
// Count items and analyze types
|
|
1961
|
+
const items = app.itemRegistry ? app.itemRegistry.getAll() : [];
|
|
1962
|
+
analysis.itemCount = items.length;
|
|
1963
|
+
|
|
1964
|
+
const animationSet = new Set();
|
|
1965
|
+
const relationSet = new Set();
|
|
1966
|
+
let hasGradient = false;
|
|
1967
|
+
let hasShadow = false;
|
|
1968
|
+
|
|
1969
|
+
items.forEach(entry => {
|
|
1970
|
+
const item = entry.item;
|
|
1971
|
+
const data = item.data || {};
|
|
1972
|
+
|
|
1973
|
+
if (entry.type === 'text' || item.className === 'PointText') analysis.hasText = true;
|
|
1974
|
+
if (item.className === 'Raster') analysis.hasImages = true;
|
|
1975
|
+
|
|
1976
|
+
if (data.animationType) {
|
|
1977
|
+
analysis.hasAnimations = true;
|
|
1978
|
+
animationSet.add(data.animationType);
|
|
1979
|
+
}
|
|
1980
|
+
// Detect keyframe animations (added via app.addAnimation with keyframes array)
|
|
1981
|
+
if (data.keyframes || data.animation || data.keyframeAnimation) {
|
|
1982
|
+
analysis.hasAnimations = true;
|
|
1983
|
+
animationSet.add('keyframe');
|
|
1984
|
+
}
|
|
1985
|
+
|
|
1986
|
+
if (item.fillColor && item.fillColor.gradient) hasGradient = true;
|
|
1987
|
+
if (item.strokeColor && item.strokeColor.gradient) hasGradient = true;
|
|
1988
|
+
if (item.shadowColor || item.shadowBlur) hasShadow = true;
|
|
1989
|
+
});
|
|
1990
|
+
|
|
1991
|
+
// Check animation manager for registered animations
|
|
1992
|
+
if (app.animationManager) {
|
|
1993
|
+
const animations = app.animationManager.getAll ? app.animationManager.getAll() : (app.animationManager.animations || []);
|
|
1994
|
+
if (animations.length > 0) {
|
|
1995
|
+
analysis.hasAnimations = true;
|
|
1996
|
+
animationSet.add('managed');
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
// Check timeline for active animations
|
|
2000
|
+
if (app.timeline && (app.timeline.isPlaying || app.timeline.animations?.length > 0)) {
|
|
2001
|
+
analysis.hasAnimations = true;
|
|
2002
|
+
animationSet.add('timeline');
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
analysis.animationTypes = Array.from(animationSet);
|
|
2006
|
+
analysis.hasGradients = hasGradient;
|
|
2007
|
+
analysis.hasShadows = hasShadow;
|
|
2008
|
+
|
|
2009
|
+
// Check relations
|
|
2010
|
+
if (app.relationRegistry) {
|
|
2011
|
+
const relations = app.relationRegistry.getAll ? app.relationRegistry.getAll() : [];
|
|
2012
|
+
analysis.hasRelations = relations.length > 0;
|
|
2013
|
+
relations.forEach(rel => {
|
|
2014
|
+
if (rel.relationType) relationSet.add(rel.relationType);
|
|
2015
|
+
});
|
|
2016
|
+
analysis.relationTypes = Array.from(relationSet);
|
|
2017
|
+
if (analysis.hasRelations) analysis.hasAnimations = true;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// Determine color complexity
|
|
2021
|
+
if (hasGradient) {
|
|
2022
|
+
analysis.colorComplexity = 'gradient';
|
|
2023
|
+
} else if (analysis.itemCount > 20 || hasShadow) {
|
|
2024
|
+
analysis.colorComplexity = 'complex';
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// Generate recommendations
|
|
2028
|
+
const recommendations = [];
|
|
2029
|
+
const { width, height } = analysis.canvasSize;
|
|
2030
|
+
const aspectRatio = width / height;
|
|
2031
|
+
|
|
2032
|
+
if (analysis.hasAnimations) {
|
|
2033
|
+
if (analysis.hasGradients || analysis.colorComplexity === 'complex') {
|
|
2034
|
+
recommendations.push({ platform: 'web', format: 'webm', confidence: 0.9, reason: 'Animated content with gradients renders best as WebM' });
|
|
2035
|
+
recommendations.push({ platform: 'instagram', format: 'mp4', confidence: 0.85, reason: 'MP4 for social media compatibility' });
|
|
2036
|
+
} else {
|
|
2037
|
+
recommendations.push({ platform: 'web', format: 'svg', confidence: 0.95, reason: 'Simple animations export well as animated SVG' });
|
|
2038
|
+
recommendations.push({ platform: 'twitter', format: 'gif', confidence: 0.8, reason: 'GIF for social media', warnings: ['Limited to 256 colors'] });
|
|
2039
|
+
}
|
|
2040
|
+
} else {
|
|
2041
|
+
if (analysis.colorComplexity === 'simple' && !analysis.hasImages) {
|
|
2042
|
+
recommendations.push({ platform: 'web', format: 'svg', confidence: 0.95, reason: 'Vector graphics best as SVG for scalability' });
|
|
2043
|
+
} else {
|
|
2044
|
+
recommendations.push({ platform: 'instagram', format: 'png', confidence: 0.9, reason: 'High quality static export as PNG' });
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
// Platform suggestions based on aspect ratio
|
|
2049
|
+
const suggestedPlatforms = [];
|
|
2050
|
+
if (Math.abs(aspectRatio - 1) < 0.1) suggestedPlatforms.push('instagram');
|
|
2051
|
+
if (aspectRatio < 0.7) suggestedPlatforms.push('instagram-story', 'tiktok');
|
|
2052
|
+
if (aspectRatio > 1.5) suggestedPlatforms.push('youtube', 'twitter', 'linkedin');
|
|
2053
|
+
suggestedPlatforms.push('web');
|
|
2054
|
+
|
|
2055
|
+
return {
|
|
2056
|
+
success: true,
|
|
2057
|
+
analysis,
|
|
2058
|
+
recommendations,
|
|
2059
|
+
suggestedPlatforms
|
|
2060
|
+
};
|
|
2061
|
+
})();
|
|
2062
|
+
`.trim();
|
|
2063
|
+
}
|
|
2064
|
+
// ===========================================================================
|
|
2065
|
+
// INTERACTIVE TRIGGER CODE GENERATORS
|
|
2066
|
+
// ===========================================================================
|
|
2067
|
+
/**
|
|
2068
|
+
* Generate code for adding a trigger
|
|
2069
|
+
*/
|
|
2070
|
+
generateAddTrigger(input) {
|
|
2071
|
+
const validated = AddTriggerInputSchema.parse(input);
|
|
2072
|
+
const { itemId, event, actions, condition, timelineOffset } = validated;
|
|
2073
|
+
const actionsStr = JSON.stringify(actions);
|
|
2074
|
+
const conditionStr = condition ? `'${condition.replace(/'/g, "\\'")}'` : 'null';
|
|
2075
|
+
const offsetStr = timelineOffset !== undefined ? timelineOffset : 'null';
|
|
2076
|
+
return `
|
|
2077
|
+
// Add trigger to ${itemId}
|
|
2078
|
+
const item = app.getItemById('${itemId}');
|
|
2079
|
+
if (!item) throw new Error('Item not found: ${itemId}');
|
|
2080
|
+
|
|
2081
|
+
const trigger = {
|
|
2082
|
+
event: '${event}',
|
|
2083
|
+
actions: ${actionsStr},
|
|
2084
|
+
condition: ${conditionStr},
|
|
2085
|
+
timelineOffset: ${offsetStr}
|
|
2086
|
+
};
|
|
2087
|
+
|
|
2088
|
+
// Initialize triggers array if needed
|
|
2089
|
+
if (!item.data) item.data = {};
|
|
2090
|
+
if (!item.data.triggers) item.data.triggers = [];
|
|
2091
|
+
|
|
2092
|
+
// Add trigger
|
|
2093
|
+
item.data.triggers.push(trigger);
|
|
2094
|
+
|
|
2095
|
+
// Register event handler
|
|
2096
|
+
if (app.triggerManager) {
|
|
2097
|
+
app.triggerManager.registerTrigger(item, trigger);
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
app.historyManager.saveState();
|
|
2101
|
+
({ success: true, itemId: '${itemId}', event: '${event}', triggerId: item.data.triggers.length - 1 });
|
|
2102
|
+
`.trim();
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* Generate code for removing a trigger
|
|
2106
|
+
*/
|
|
2107
|
+
generateRemoveTrigger(input) {
|
|
2108
|
+
const validated = RemoveTriggerInputSchema.parse(input);
|
|
2109
|
+
const { itemId, event, removeAll } = validated;
|
|
2110
|
+
if (removeAll) {
|
|
2111
|
+
return `
|
|
2112
|
+
// Remove all triggers from ${itemId}
|
|
2113
|
+
const item = app.getItemById('${itemId}');
|
|
2114
|
+
if (!item) throw new Error('Item not found: ${itemId}');
|
|
2115
|
+
|
|
2116
|
+
const removedCount = item.data?.triggers?.length || 0;
|
|
2117
|
+
if (item.data) item.data.triggers = [];
|
|
2118
|
+
|
|
2119
|
+
if (app.triggerManager) {
|
|
2120
|
+
app.triggerManager.unregisterAll(item);
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
app.historyManager.saveState();
|
|
2124
|
+
({ success: true, itemId: '${itemId}', removedCount, removeAll: true });
|
|
2125
|
+
`.trim();
|
|
2126
|
+
}
|
|
2127
|
+
return `
|
|
2128
|
+
// Remove ${event} trigger from ${itemId}
|
|
2129
|
+
const item = app.getItemById('${itemId}');
|
|
2130
|
+
if (!item) throw new Error('Item not found: ${itemId}');
|
|
2131
|
+
|
|
2132
|
+
let removedCount = 0;
|
|
2133
|
+
if (item.data?.triggers) {
|
|
2134
|
+
const before = item.data.triggers.length;
|
|
2135
|
+
item.data.triggers = item.data.triggers.filter(t => t.event !== '${event}');
|
|
2136
|
+
removedCount = before - item.data.triggers.length;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
if (app.triggerManager) {
|
|
2140
|
+
app.triggerManager.unregisterEvent(item, '${event}');
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
app.historyManager.saveState();
|
|
2144
|
+
({ success: true, itemId: '${itemId}', event: '${event}', removedCount });
|
|
2145
|
+
`.trim();
|
|
2146
|
+
}
|
|
2147
|
+
/**
|
|
2148
|
+
* Generate code for querying triggers
|
|
2149
|
+
*/
|
|
2150
|
+
generateQueryTriggers(input) {
|
|
2151
|
+
const validated = QueryTriggersInputSchema.parse(input);
|
|
2152
|
+
const { itemId, event } = validated;
|
|
2153
|
+
if (itemId) {
|
|
2154
|
+
return `
|
|
2155
|
+
// Query triggers for ${itemId}
|
|
2156
|
+
const item = app.getItemById('${itemId}');
|
|
2157
|
+
if (!item) throw new Error('Item not found: ${itemId}');
|
|
2158
|
+
|
|
2159
|
+
let triggers = item.data?.triggers || [];
|
|
2160
|
+
${event ? `triggers = triggers.filter(t => t.event === '${event}');` : ''}
|
|
2161
|
+
|
|
2162
|
+
({ success: true, itemId: '${itemId}', triggers, count: triggers.length });
|
|
2163
|
+
`.trim();
|
|
2164
|
+
}
|
|
2165
|
+
return `
|
|
2166
|
+
// Query all triggers
|
|
2167
|
+
const allTriggers = [];
|
|
2168
|
+
const items = app.itemRegistry ? app.itemRegistry.getAll() : [];
|
|
2169
|
+
|
|
2170
|
+
items.forEach(entry => {
|
|
2171
|
+
const item = entry.item;
|
|
2172
|
+
if (item.data?.triggers?.length > 0) {
|
|
2173
|
+
let triggers = item.data.triggers;
|
|
2174
|
+
${event ? `triggers = triggers.filter(t => t.event === '${event}');` : ''}
|
|
2175
|
+
triggers.forEach((trigger, idx) => {
|
|
2176
|
+
allTriggers.push({
|
|
2177
|
+
itemId: entry.id,
|
|
2178
|
+
triggerId: idx,
|
|
2179
|
+
...trigger
|
|
2180
|
+
});
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
2183
|
+
});
|
|
2184
|
+
|
|
2185
|
+
({ success: true, triggers: allTriggers, count: allTriggers.length${event ? `, event: '${event}'` : ''} });
|
|
2186
|
+
`.trim();
|
|
2187
|
+
}
|
|
2188
|
+
// ===========================================================================
|
|
2189
|
+
// QUIZ CODE GENERATORS
|
|
2190
|
+
// ===========================================================================
|
|
2191
|
+
/**
|
|
2192
|
+
* Generate code for creating a quiz
|
|
2193
|
+
*/
|
|
2194
|
+
generateCreateQuiz(input) {
|
|
2195
|
+
const validated = CreateQuizInputSchema.parse(input);
|
|
2196
|
+
const { title, questions, passingScore, showScore, allowRetry, shuffleQuestions, shuffleOptions } = validated;
|
|
2197
|
+
const quizConfig = JSON.stringify({
|
|
2198
|
+
title: title || 'Untitled Quiz',
|
|
2199
|
+
questions,
|
|
2200
|
+
passingScore: passingScore || 70,
|
|
2201
|
+
showScore: showScore !== false,
|
|
2202
|
+
allowRetry: allowRetry !== false,
|
|
2203
|
+
shuffleQuestions: shuffleQuestions || false,
|
|
2204
|
+
shuffleOptions: shuffleOptions || false,
|
|
2205
|
+
}, null, 2);
|
|
2206
|
+
return `
|
|
2207
|
+
// Create quiz
|
|
2208
|
+
const quizConfig = ${quizConfig};
|
|
2209
|
+
|
|
2210
|
+
// Initialize quiz manager if needed
|
|
2211
|
+
if (!app.quizManager) {
|
|
2212
|
+
app.quizManager = {
|
|
2213
|
+
quizzes: [],
|
|
2214
|
+
activeQuiz: null,
|
|
2215
|
+
createQuiz: function(config) {
|
|
2216
|
+
const quiz = {
|
|
2217
|
+
id: 'quiz_' + Date.now(),
|
|
2218
|
+
...config,
|
|
2219
|
+
state: {
|
|
2220
|
+
currentQuestion: 0,
|
|
2221
|
+
answers: [],
|
|
2222
|
+
score: 0,
|
|
2223
|
+
maxScore: config.questions.reduce((sum, q) => sum + q.points, 0),
|
|
2224
|
+
complete: false
|
|
2225
|
+
}
|
|
2226
|
+
};
|
|
2227
|
+
this.quizzes.push(quiz);
|
|
2228
|
+
this.activeQuiz = quiz;
|
|
2229
|
+
return quiz;
|
|
2230
|
+
}
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
const quiz = app.quizManager.createQuiz(quizConfig);
|
|
2235
|
+
app.historyManager.saveState();
|
|
2236
|
+
|
|
2237
|
+
({
|
|
2238
|
+
success: true,
|
|
2239
|
+
quizId: quiz.id,
|
|
2240
|
+
title: quiz.title,
|
|
2241
|
+
questionCount: quiz.questions.length,
|
|
2242
|
+
maxScore: quiz.state.maxScore,
|
|
2243
|
+
passingScore: quiz.passingScore
|
|
2244
|
+
});
|
|
2245
|
+
`.trim();
|
|
2246
|
+
}
|
|
2247
|
+
/**
|
|
2248
|
+
* Generate code for getting quiz state
|
|
2249
|
+
*/
|
|
2250
|
+
generateGetQuizState(input) {
|
|
2251
|
+
const validated = GetQuizStateInputSchema.parse(input);
|
|
2252
|
+
const { quizId } = validated;
|
|
2253
|
+
const quizRef = quizId
|
|
2254
|
+
? `app.quizManager.quizzes.find(q => q.id === '${quizId}')`
|
|
2255
|
+
: 'app.quizManager?.activeQuiz';
|
|
2256
|
+
return `
|
|
2257
|
+
// Get quiz state
|
|
2258
|
+
if (!app.quizManager) {
|
|
2259
|
+
return { success: false, error: 'No quiz manager initialized' };
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
const quiz = ${quizRef};
|
|
2263
|
+
if (!quiz) {
|
|
2264
|
+
return { success: false, error: 'Quiz not found' };
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
({
|
|
2268
|
+
success: true,
|
|
2269
|
+
quizId: quiz.id,
|
|
2270
|
+
title: quiz.title,
|
|
2271
|
+
currentQuestion: quiz.state.currentQuestion,
|
|
2272
|
+
totalQuestions: quiz.questions.length,
|
|
2273
|
+
score: quiz.state.score,
|
|
2274
|
+
maxScore: quiz.state.maxScore,
|
|
2275
|
+
answers: quiz.state.answers,
|
|
2276
|
+
complete: quiz.state.complete,
|
|
2277
|
+
passed: quiz.state.complete ? (quiz.state.score / quiz.state.maxScore * 100) >= quiz.passingScore : null
|
|
2278
|
+
});
|
|
2279
|
+
`.trim();
|
|
2280
|
+
}
|
|
2281
|
+
/**
|
|
2282
|
+
* Generate code for resetting a quiz
|
|
2283
|
+
*/
|
|
2284
|
+
generateResetQuiz(input) {
|
|
2285
|
+
const validated = ResetQuizInputSchema.parse(input);
|
|
2286
|
+
const { quizId } = validated;
|
|
2287
|
+
const quizRef = quizId
|
|
2288
|
+
? `app.quizManager.quizzes.find(q => q.id === '${quizId}')`
|
|
2289
|
+
: 'app.quizManager?.activeQuiz';
|
|
2290
|
+
return `
|
|
2291
|
+
// Reset quiz
|
|
2292
|
+
if (!app.quizManager) {
|
|
2293
|
+
return { success: false, error: 'No quiz manager initialized' };
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
const quiz = ${quizRef};
|
|
2297
|
+
if (!quiz) {
|
|
2298
|
+
return { success: false, error: 'Quiz not found' };
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
// Reset state
|
|
2302
|
+
quiz.state = {
|
|
2303
|
+
currentQuestion: 0,
|
|
2304
|
+
answers: [],
|
|
2305
|
+
score: 0,
|
|
2306
|
+
maxScore: quiz.questions.reduce((sum, q) => sum + q.points, 0),
|
|
2307
|
+
complete: false
|
|
2308
|
+
};
|
|
2309
|
+
|
|
2310
|
+
app.historyManager.saveState();
|
|
2311
|
+
|
|
2312
|
+
({ success: true, quizId: quiz.id, reset: true });
|
|
2313
|
+
`.trim();
|
|
2314
|
+
}
|
|
2315
|
+
// ===========================================================================
|
|
2316
|
+
// LETTER COLLAGE CODE GENERATORS
|
|
2317
|
+
// ===========================================================================
|
|
2318
|
+
/**
|
|
2319
|
+
* Generate code to create a letter collage
|
|
2320
|
+
*/
|
|
2321
|
+
generateCreateLetterCollage(input) {
|
|
2322
|
+
const validated = CreateLetterCollageInputSchema.parse(input);
|
|
2323
|
+
const { text, style = 'tile', palette, position, fontSize = 48, fontFamily = 'Inter, sans-serif', spacing = 1.1, gradientPalette, gradientDirection = 'vertical', cornerRadius = 4, shadowEnabled = true, } = validated;
|
|
2324
|
+
// Build options object
|
|
2325
|
+
const options = {
|
|
2326
|
+
style,
|
|
2327
|
+
fontSize,
|
|
2328
|
+
fontFamily,
|
|
2329
|
+
spacing,
|
|
2330
|
+
cornerRadius,
|
|
2331
|
+
shadowEnabled,
|
|
2332
|
+
};
|
|
2333
|
+
if (palette)
|
|
2334
|
+
options.palette = palette;
|
|
2335
|
+
if (position)
|
|
2336
|
+
options.position = position;
|
|
2337
|
+
if (gradientPalette)
|
|
2338
|
+
options.gradientPalette = gradientPalette;
|
|
2339
|
+
if (gradientDirection)
|
|
2340
|
+
options.gradientDirection = gradientDirection;
|
|
2341
|
+
return `
|
|
2342
|
+
// Create letter collage: "${text}"
|
|
2343
|
+
(function() {
|
|
2344
|
+
const text = ${JSON.stringify(text)};
|
|
2345
|
+
const options = ${JSON.stringify(options, null, 2)};
|
|
2346
|
+
|
|
2347
|
+
if (!app.createLetterCollage) {
|
|
2348
|
+
return { success: false, error: 'Letter collage feature not available' };
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
const result = app.createLetterCollage(text, options);
|
|
2352
|
+
|
|
2353
|
+
if (app.historyManager) {
|
|
2354
|
+
app.historyManager.saveState('create_letter_collage');
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
return result;
|
|
2358
|
+
})();
|
|
2359
|
+
`.trim();
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Generate code to animate a letter collage
|
|
2363
|
+
*/
|
|
2364
|
+
generateAnimateLetterCollage(input) {
|
|
2365
|
+
const validated = AnimateLetterCollageInputSchema.parse(input);
|
|
2366
|
+
const { collageId, animationType, staggerDelay = 0.1, animationSpeed = 1, } = validated;
|
|
2367
|
+
return `
|
|
2368
|
+
// Animate letter collage: ${collageId}
|
|
2369
|
+
(function() {
|
|
2370
|
+
const collageId = ${JSON.stringify(collageId)};
|
|
2371
|
+
const animationType = ${JSON.stringify(animationType)};
|
|
2372
|
+
const staggerDelay = ${staggerDelay};
|
|
2373
|
+
const animationSpeed = ${animationSpeed};
|
|
2374
|
+
|
|
2375
|
+
if (!app.animateLetterCollage) {
|
|
2376
|
+
return { success: false, error: 'Letter collage animation not available' };
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
const result = app.animateLetterCollage(collageId, {
|
|
2380
|
+
animationType,
|
|
2381
|
+
staggerDelay,
|
|
2382
|
+
animationSpeed
|
|
2383
|
+
});
|
|
2384
|
+
|
|
2385
|
+
return result;
|
|
2386
|
+
})();
|
|
2387
|
+
`.trim();
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Generate code to get letter collage options
|
|
2391
|
+
*/
|
|
2392
|
+
generateGetLetterCollageOptions() {
|
|
2393
|
+
return `
|
|
2394
|
+
// Get letter collage options
|
|
2395
|
+
(function() {
|
|
2396
|
+
if (!app.getLetterCollageOptions) {
|
|
2397
|
+
// Return static options if function not available
|
|
2398
|
+
return {
|
|
2399
|
+
success: true,
|
|
2400
|
+
styles: ['tile', 'magazine', 'paperCut', 'fold', 'gradient', 'image'],
|
|
2401
|
+
tilePalettes: {
|
|
2402
|
+
game: ['wordle', 'scrabble'],
|
|
2403
|
+
vibrant: ['candy', 'neon', 'rainbow'],
|
|
2404
|
+
soft: ['pastel', 'cotton'],
|
|
2405
|
+
natural: ['earth', 'ocean', 'forest', 'sunset'],
|
|
2406
|
+
professional: ['corporate', 'minimal', 'slate'],
|
|
2407
|
+
seasonal: ['christmas', 'halloween', 'spring'],
|
|
2408
|
+
magazine: ['magazine', 'newspaper', 'vintage'],
|
|
2409
|
+
paperCraft: ['paperCraft', 'origami', 'craftPaper']
|
|
2410
|
+
},
|
|
2411
|
+
gradientPalettes: ['rainbow', 'sunset', 'ocean', 'fire', 'gold', 'rose', 'ice', 'cyberpunk', 'neonGlow', 'purplePink'],
|
|
2412
|
+
gradientDirections: ['vertical', 'horizontal', 'diagonal', 'radial']
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
return app.getLetterCollageOptions();
|
|
2417
|
+
})();
|
|
2418
|
+
`.trim();
|
|
2419
|
+
}
|
|
2420
|
+
// ===========================================================================
|
|
2421
|
+
// CANVAS PRESETS CODE GENERATORS
|
|
2422
|
+
// ===========================================================================
|
|
2423
|
+
/**
|
|
2424
|
+
* Generate code to get canvas presets
|
|
2425
|
+
*/
|
|
2426
|
+
generateGetCanvasPresets() {
|
|
2427
|
+
return `
|
|
2428
|
+
// Get canvas presets
|
|
2429
|
+
(function() {
|
|
2430
|
+
if (app.getCanvasPresets) {
|
|
2431
|
+
return app.getCanvasPresets();
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
// Return static presets if function not available
|
|
2435
|
+
return {
|
|
2436
|
+
success: true,
|
|
2437
|
+
presets: [
|
|
2438
|
+
{ key: 'youtube-thumbnail', name: 'YouTube Thumbnail', width: 1280, height: 720, aspectRatio: '16:9', category: 'video' },
|
|
2439
|
+
{ key: 'youtube-short', name: 'YouTube Shorts', width: 1080, height: 1920, aspectRatio: '9:16', category: 'video' },
|
|
2440
|
+
{ key: 'tiktok', name: 'TikTok', width: 1080, height: 1920, aspectRatio: '9:16', category: 'video' },
|
|
2441
|
+
{ key: 'instagram-story', name: 'Instagram Story', width: 1080, height: 1920, aspectRatio: '9:16', category: 'social' },
|
|
2442
|
+
{ key: 'instagram-post', name: 'Instagram Post (Square)', width: 1080, height: 1080, aspectRatio: '1:1', category: 'social' },
|
|
2443
|
+
{ key: 'instagram-landscape', name: 'Instagram Landscape', width: 1080, height: 566, aspectRatio: '1.91:1', category: 'social' },
|
|
2444
|
+
{ key: 'instagram-portrait', name: 'Instagram Portrait', width: 1080, height: 1350, aspectRatio: '4:5', category: 'social' },
|
|
2445
|
+
{ key: 'facebook-post', name: 'Facebook Post', width: 1200, height: 630, aspectRatio: '1.91:1', category: 'social' },
|
|
2446
|
+
{ key: 'facebook-cover', name: 'Facebook Cover', width: 820, height: 312, aspectRatio: '2.63:1', category: 'social' },
|
|
2447
|
+
{ key: 'facebook-story', name: 'Facebook Story', width: 1080, height: 1920, aspectRatio: '9:16', category: 'social' },
|
|
2448
|
+
{ key: 'twitter-post', name: 'Twitter/X Post', width: 1200, height: 675, aspectRatio: '16:9', category: 'social' },
|
|
2449
|
+
{ key: 'twitter-header', name: 'Twitter/X Header', width: 1500, height: 500, aspectRatio: '3:1', category: 'social' },
|
|
2450
|
+
{ key: 'linkedin-post', name: 'LinkedIn Post', width: 1200, height: 627, aspectRatio: '1.91:1', category: 'social' },
|
|
2451
|
+
{ key: 'linkedin-banner', name: 'LinkedIn Banner', width: 1584, height: 396, aspectRatio: '4:1', category: 'social' },
|
|
2452
|
+
{ key: 'pinterest-pin', name: 'Pinterest Pin', width: 1000, height: 1500, aspectRatio: '2:3', category: 'social' },
|
|
2453
|
+
{ key: 'presentation-16x9', name: 'Presentation 16:9', width: 1920, height: 1080, aspectRatio: '16:9', category: 'presentation' },
|
|
2454
|
+
{ key: 'presentation-4x3', name: 'Presentation 4:3', width: 1024, height: 768, aspectRatio: '4:3', category: 'presentation' },
|
|
2455
|
+
{ key: 'hd-720p', name: 'HD 720p', width: 1280, height: 720, aspectRatio: '16:9', category: 'video' },
|
|
2456
|
+
{ key: 'full-hd-1080p', name: 'Full HD 1080p', width: 1920, height: 1080, aspectRatio: '16:9', category: 'video' },
|
|
2457
|
+
{ key: 'default', name: 'Default', width: 800, height: 600, aspectRatio: '4:3', category: 'general' }
|
|
2458
|
+
]
|
|
2459
|
+
};
|
|
2460
|
+
})();
|
|
2461
|
+
`.trim();
|
|
2462
|
+
}
|
|
2463
|
+
// ===========================================================================
|
|
2464
|
+
// MAP CODE GENERATORS
|
|
2465
|
+
// ===========================================================================
|
|
2466
|
+
/**
|
|
2467
|
+
* Generate code to load a geographic map
|
|
2468
|
+
*/
|
|
2469
|
+
generateLoadMap(input) {
|
|
2470
|
+
const validated = LoadMapInputSchema.parse(input);
|
|
2471
|
+
const { mapId, ...mapOptions } = validated;
|
|
2472
|
+
const optionsStr = Object.keys(mapOptions).length > 0 ? JSON.stringify(mapOptions) : '{}';
|
|
2473
|
+
return `
|
|
2474
|
+
// Load geographic map
|
|
2475
|
+
(async function() {
|
|
2476
|
+
if (!app.mapSystem) {
|
|
2477
|
+
return { success: false, error: 'Map system not available' };
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
try {
|
|
2481
|
+
const result = await app.mapSystem.loadMap('${mapId}', ${optionsStr});
|
|
2482
|
+
return {
|
|
2483
|
+
success: true,
|
|
2484
|
+
mapId: result.mapId || '${mapId}',
|
|
2485
|
+
regions: result.regions?.length || 0,
|
|
2486
|
+
bounds: result.bounds,
|
|
2487
|
+
center: result.center
|
|
2488
|
+
};
|
|
2489
|
+
} catch (error) {
|
|
2490
|
+
return { success: false, error: error.message };
|
|
2491
|
+
}
|
|
2492
|
+
})();
|
|
2493
|
+
`.trim();
|
|
2494
|
+
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Generate code to highlight map regions
|
|
2497
|
+
*/
|
|
2498
|
+
generateHighlightRegions(input) {
|
|
2499
|
+
const validated = HighlightRegionsInputSchema.parse(input);
|
|
2500
|
+
const regionIds = JSON.stringify(validated.regionIds);
|
|
2501
|
+
const optionsStr = validated.options ? JSON.stringify(validated.options) : '{}';
|
|
2502
|
+
return `
|
|
2503
|
+
// Highlight map regions
|
|
2504
|
+
(function() {
|
|
2505
|
+
if (!app.mapSystem) {
|
|
2506
|
+
return { success: false, error: 'Map system not available' };
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
try {
|
|
2510
|
+
app.mapSystem.highlightRegions(${regionIds}, ${optionsStr});
|
|
2511
|
+
return { success: true, highlighted: ${regionIds} };
|
|
2512
|
+
} catch (error) {
|
|
2513
|
+
return { success: false, error: error.message };
|
|
2514
|
+
}
|
|
2515
|
+
})();
|
|
2516
|
+
`.trim();
|
|
2517
|
+
}
|
|
2518
|
+
/**
|
|
2519
|
+
* Generate code to unhighlight map regions
|
|
2520
|
+
*/
|
|
2521
|
+
generateUnhighlightRegions(input) {
|
|
2522
|
+
const validated = UnhighlightRegionsInputSchema.parse(input);
|
|
2523
|
+
const regionIds = validated.regionIds === 'all' ? "'all'" : JSON.stringify(validated.regionIds);
|
|
2524
|
+
return `
|
|
2525
|
+
// Unhighlight map regions
|
|
2526
|
+
(function() {
|
|
2527
|
+
if (!app.mapSystem) {
|
|
2528
|
+
return { success: false, error: 'Map system not available' };
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
try {
|
|
2532
|
+
app.mapSystem.unhighlightRegions(${regionIds});
|
|
2533
|
+
return { success: true };
|
|
2534
|
+
} catch (error) {
|
|
2535
|
+
return { success: false, error: error.message };
|
|
2536
|
+
}
|
|
2537
|
+
})();
|
|
2538
|
+
`.trim();
|
|
2539
|
+
}
|
|
2540
|
+
/**
|
|
2541
|
+
* Generate code to apply data-driven colors (choropleth)
|
|
2542
|
+
*/
|
|
2543
|
+
generateApplyDataColors(input) {
|
|
2544
|
+
const validated = ApplyDataColorsInputSchema.parse(input);
|
|
2545
|
+
const dataStr = JSON.stringify(validated.data);
|
|
2546
|
+
const optionsStr = validated.options ? JSON.stringify(validated.options) : '{}';
|
|
2547
|
+
return `
|
|
2548
|
+
// Apply choropleth data colors
|
|
2549
|
+
(function() {
|
|
2550
|
+
if (!app.mapSystem) {
|
|
2551
|
+
return { success: false, error: 'Map system not available' };
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
try {
|
|
2555
|
+
app.mapSystem.applyDataColors(${dataStr}, ${optionsStr});
|
|
2556
|
+
return { success: true, regionsColored: ${Object.keys(validated.data).length} };
|
|
2557
|
+
} catch (error) {
|
|
2558
|
+
return { success: false, error: error.message };
|
|
2559
|
+
}
|
|
2560
|
+
})();
|
|
2561
|
+
`.trim();
|
|
2562
|
+
}
|
|
2563
|
+
/**
|
|
2564
|
+
* Generate code to add a map marker
|
|
2565
|
+
*/
|
|
2566
|
+
generateAddMarker(input) {
|
|
2567
|
+
const validated = AddMarkerInputSchema.parse(input);
|
|
2568
|
+
const options = {
|
|
2569
|
+
lat: validated.lat,
|
|
2570
|
+
lon: validated.lon,
|
|
2571
|
+
};
|
|
2572
|
+
if (validated.label)
|
|
2573
|
+
options.label = validated.label;
|
|
2574
|
+
if (validated.color)
|
|
2575
|
+
options.color = validated.color;
|
|
2576
|
+
if (validated.size)
|
|
2577
|
+
options.size = validated.size;
|
|
2578
|
+
if (validated.pulse !== undefined)
|
|
2579
|
+
options.pulse = validated.pulse;
|
|
2580
|
+
if (validated.shape)
|
|
2581
|
+
options.shape = validated.shape;
|
|
2582
|
+
return `
|
|
2583
|
+
// Add map marker
|
|
2584
|
+
(function() {
|
|
2585
|
+
if (!app.mapSystem) {
|
|
2586
|
+
return { success: false, error: 'Map system not available' };
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
try {
|
|
2590
|
+
const marker = app.mapSystem.addMarker(${JSON.stringify(options)});
|
|
2591
|
+
return { success: true, markerId: marker?.id || 'marker_added' };
|
|
2592
|
+
} catch (error) {
|
|
2593
|
+
return { success: false, error: error.message };
|
|
2594
|
+
}
|
|
2595
|
+
})();
|
|
2596
|
+
`.trim();
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* Generate code to add map labels
|
|
2600
|
+
*/
|
|
2601
|
+
generateAddMapLabels(input) {
|
|
2602
|
+
const validated = AddMapLabelsInputSchema.parse(input);
|
|
2603
|
+
const regionsStr = validated.regions ? JSON.stringify(validated.regions) : 'null';
|
|
2604
|
+
const optionsStr = validated.options ? JSON.stringify(validated.options) : '{}';
|
|
2605
|
+
return `
|
|
2606
|
+
// Add map labels
|
|
2607
|
+
(function() {
|
|
2608
|
+
if (!app.mapSystem) {
|
|
2609
|
+
return { success: false, error: 'Map system not available' };
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
try {
|
|
2613
|
+
app.mapSystem.addLabels({ regions: ${regionsStr}, ...${optionsStr} });
|
|
2614
|
+
return { success: true };
|
|
2615
|
+
} catch (error) {
|
|
2616
|
+
return { success: false, error: error.message };
|
|
2617
|
+
}
|
|
2618
|
+
})();
|
|
2619
|
+
`.trim();
|
|
2620
|
+
}
|
|
2621
|
+
/**
|
|
2622
|
+
* Generate code to pan the map
|
|
2623
|
+
*/
|
|
2624
|
+
generatePanMap(input) {
|
|
2625
|
+
const validated = PanMapInputSchema.parse(input);
|
|
2626
|
+
const options = {};
|
|
2627
|
+
if (validated.animate !== undefined)
|
|
2628
|
+
options.animate = validated.animate;
|
|
2629
|
+
if (validated.duration !== undefined)
|
|
2630
|
+
options.duration = validated.duration;
|
|
2631
|
+
return `
|
|
2632
|
+
// Pan map to coordinates
|
|
2633
|
+
(function() {
|
|
2634
|
+
if (!app.mapSystem) {
|
|
2635
|
+
return { success: false, error: 'Map system not available' };
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
try {
|
|
2639
|
+
app.mapSystem.panTo(${validated.lat}, ${validated.lon}${Object.keys(options).length > 0 ? ', ' + JSON.stringify(options) : ''});
|
|
2640
|
+
return { success: true, panTo: [${validated.lat}, ${validated.lon}] };
|
|
2641
|
+
} catch (error) {
|
|
2642
|
+
return { success: false, error: error.message };
|
|
2643
|
+
}
|
|
2644
|
+
})();
|
|
2645
|
+
`.trim();
|
|
2646
|
+
}
|
|
2647
|
+
/**
|
|
2648
|
+
* Generate code to zoom the map
|
|
2649
|
+
*/
|
|
2650
|
+
generateZoomMap(input) {
|
|
2651
|
+
const validated = ZoomMapInputSchema.parse(input);
|
|
2652
|
+
const options = {};
|
|
2653
|
+
if (validated.animate !== undefined)
|
|
2654
|
+
options.animate = validated.animate;
|
|
2655
|
+
if (validated.duration !== undefined)
|
|
2656
|
+
options.duration = validated.duration;
|
|
2657
|
+
return `
|
|
2658
|
+
// Set map zoom level
|
|
2659
|
+
(function() {
|
|
2660
|
+
if (!app.mapSystem) {
|
|
2661
|
+
return { success: false, error: 'Map system not available' };
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
try {
|
|
2665
|
+
app.mapSystem.zoomTo(${validated.level}${Object.keys(options).length > 0 ? ', ' + JSON.stringify(options) : ''});
|
|
2666
|
+
return { success: true, zoomLevel: ${validated.level} };
|
|
2667
|
+
} catch (error) {
|
|
2668
|
+
return { success: false, error: error.message };
|
|
2669
|
+
}
|
|
2670
|
+
})();
|
|
2671
|
+
`.trim();
|
|
2672
|
+
}
|
|
2673
|
+
/**
|
|
2674
|
+
* Generate code to export map configuration
|
|
2675
|
+
*/
|
|
2676
|
+
generateExportMap() {
|
|
2677
|
+
return `
|
|
2678
|
+
// Export map configuration
|
|
2679
|
+
(function() {
|
|
2680
|
+
if (!app.mapSystem) {
|
|
2681
|
+
return { success: false, error: 'Map system not available' };
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
try {
|
|
2685
|
+
const mapData = app.mapSystem.exportMap();
|
|
2686
|
+
return { success: true, ...mapData };
|
|
2687
|
+
} catch (error) {
|
|
2688
|
+
return { success: false, error: error.message };
|
|
2689
|
+
}
|
|
2690
|
+
})();
|
|
2691
|
+
`.trim();
|
|
2692
|
+
}
|
|
2693
|
+
/**
|
|
2694
|
+
* Generate code to import a custom map
|
|
2695
|
+
*/
|
|
2696
|
+
generateImportCustomMap(input) {
|
|
2697
|
+
const validated = ImportCustomMapInputSchema.parse(input);
|
|
2698
|
+
const optionsStr = validated.options ? JSON.stringify(validated.options) : '{}';
|
|
2699
|
+
if (validated.url) {
|
|
2700
|
+
return `
|
|
2701
|
+
// Import custom map from URL
|
|
2702
|
+
(async function() {
|
|
2703
|
+
if (!app.mapSystem) {
|
|
2704
|
+
return { success: false, error: 'Map system not available' };
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
try {
|
|
2708
|
+
const result = await app.mapSystem.importCustomMap('${validated.url}', ${optionsStr});
|
|
2709
|
+
return { success: true, ...result };
|
|
2710
|
+
} catch (error) {
|
|
2711
|
+
return { success: false, error: error.message };
|
|
2712
|
+
}
|
|
2713
|
+
})();
|
|
2714
|
+
`.trim();
|
|
2715
|
+
}
|
|
2716
|
+
else if (validated.geoJson) {
|
|
2717
|
+
return `
|
|
2718
|
+
// Import custom GeoJSON map
|
|
2719
|
+
(async function() {
|
|
2720
|
+
if (!app.mapSystem) {
|
|
2721
|
+
return { success: false, error: 'Map system not available' };
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
try {
|
|
2725
|
+
const result = await app.mapSystem.importCustomMap(${JSON.stringify(validated.geoJson)}, ${optionsStr});
|
|
2726
|
+
return { success: true, ...result };
|
|
2727
|
+
} catch (error) {
|
|
2728
|
+
return { success: false, error: error.message };
|
|
2729
|
+
}
|
|
2730
|
+
})();
|
|
2731
|
+
`.trim();
|
|
2732
|
+
}
|
|
2733
|
+
return `
|
|
2734
|
+
// Import custom map - no source provided
|
|
2735
|
+
(function() {
|
|
2736
|
+
return { success: false, error: 'Either url or geoJson must be provided' };
|
|
2737
|
+
})();
|
|
2738
|
+
`.trim();
|
|
2739
|
+
}
|
|
2740
|
+
/**
|
|
2741
|
+
* Generate code to get region at a point (hit testing)
|
|
2742
|
+
*/
|
|
2743
|
+
generateGetRegionAtPoint(input) {
|
|
2744
|
+
const validated = GetRegionAtPointInputSchema.parse(input);
|
|
2745
|
+
return `
|
|
2746
|
+
// Get region at canvas point
|
|
2747
|
+
(function() {
|
|
2748
|
+
if (!app.mapSystem) {
|
|
2749
|
+
return { success: false, error: 'Map system not available' };
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
try {
|
|
2753
|
+
const region = app.mapSystem.getRegionAtPoint({ x: ${validated.x}, y: ${validated.y} });
|
|
2754
|
+
if (region) {
|
|
2755
|
+
return { success: true, regionId: region.id, regionName: region.name, properties: region.properties };
|
|
2756
|
+
}
|
|
2757
|
+
return { success: true, regionId: null, message: 'No region at this point' };
|
|
2758
|
+
} catch (error) {
|
|
2759
|
+
return { success: false, error: error.message };
|
|
2760
|
+
}
|
|
2761
|
+
})();
|
|
2762
|
+
`.trim();
|
|
2763
|
+
}
|
|
2764
|
+
// ===========================================================================
|
|
2765
|
+
// MAP ANIMATION/CSV CODE GENERATORS
|
|
2766
|
+
// ===========================================================================
|
|
2767
|
+
/**
|
|
2768
|
+
* Generate code to animate map regions with keyframes
|
|
2769
|
+
*/
|
|
2770
|
+
generateAnimateMapRegions(input) {
|
|
2771
|
+
const validated = AnimateMapRegionsInputSchema.parse(input);
|
|
2772
|
+
const regionsStr = JSON.stringify(validated.regions);
|
|
2773
|
+
return `
|
|
2774
|
+
// Animate map regions with keyframes
|
|
2775
|
+
(function() {
|
|
2776
|
+
if (!app.mapSystem) {
|
|
2777
|
+
return { success: false, error: 'Map system not available' };
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
try {
|
|
2781
|
+
const result = app.mapSystem.animateRegions({
|
|
2782
|
+
duration: ${validated.duration || 5},
|
|
2783
|
+
loop: ${validated.loop !== false},
|
|
2784
|
+
regions: ${regionsStr}
|
|
2785
|
+
});
|
|
2786
|
+
return { success: true, animatedRegions: Object.keys(${regionsStr}), duration: ${validated.duration || 5}, errors: result?.errors || [] };
|
|
2787
|
+
} catch (error) {
|
|
2788
|
+
return { success: false, error: error.message };
|
|
2789
|
+
}
|
|
2790
|
+
})();
|
|
2791
|
+
`.trim();
|
|
2792
|
+
}
|
|
2793
|
+
/**
|
|
2794
|
+
* Generate code to create wave animation across map regions
|
|
2795
|
+
*/
|
|
2796
|
+
generateAnimateMapWave(input) {
|
|
2797
|
+
const validated = AnimateMapWaveInputSchema.parse(input);
|
|
2798
|
+
const colorsStr = validated.colors ? JSON.stringify(validated.colors) : '["#ef4444", "#fbbf24", "#22c55e", "#3b82f6"]';
|
|
2799
|
+
return `
|
|
2800
|
+
// Create wave animation across map regions
|
|
2801
|
+
(function() {
|
|
2802
|
+
if (!app.mapSystem) {
|
|
2803
|
+
return { success: false, error: 'Map system not available' };
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
try {
|
|
2807
|
+
const result = app.mapSystem.animateWave({
|
|
2808
|
+
duration: ${validated.duration || 10},
|
|
2809
|
+
loop: ${validated.loop !== false},
|
|
2810
|
+
colors: ${colorsStr},
|
|
2811
|
+
waveDirection: '${validated.waveDirection || 'horizontal'}'
|
|
2812
|
+
});
|
|
2813
|
+
return { success: true, animatedRegions: result?.animatedRegions || [], totalRegions: result?.totalRegions || 0 };
|
|
2814
|
+
} catch (error) {
|
|
2815
|
+
return { success: false, error: error.message };
|
|
2816
|
+
}
|
|
2817
|
+
})();
|
|
2818
|
+
`.trim();
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Generate code to stop map animations
|
|
2822
|
+
*/
|
|
2823
|
+
generateStopMapAnimations(input) {
|
|
2824
|
+
const validated = StopMapAnimationsInputSchema.parse(input);
|
|
2825
|
+
const regionsStr = validated.regions ? JSON.stringify(validated.regions) : 'null';
|
|
2826
|
+
return `
|
|
2827
|
+
// Stop map region animations
|
|
2828
|
+
(function() {
|
|
2829
|
+
if (!app.mapSystem) {
|
|
2830
|
+
return { success: false, error: 'Map system not available' };
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
try {
|
|
2834
|
+
app.mapSystem.stopAnimations({
|
|
2835
|
+
regions: ${regionsStr},
|
|
2836
|
+
resetColors: ${validated.resetColors !== false}
|
|
2837
|
+
});
|
|
2838
|
+
return { success: true };
|
|
2839
|
+
} catch (error) {
|
|
2840
|
+
return { success: false, error: error.message };
|
|
2841
|
+
}
|
|
2842
|
+
})();
|
|
2843
|
+
`.trim();
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* Generate code to get animated map regions
|
|
2847
|
+
*/
|
|
2848
|
+
generateGetAnimatedMapRegions() {
|
|
2849
|
+
return `
|
|
2850
|
+
// Get animated map regions
|
|
2851
|
+
(function() {
|
|
2852
|
+
if (!app.mapSystem) {
|
|
2853
|
+
return { success: false, error: 'Map system not available' };
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
try {
|
|
2857
|
+
const result = app.mapSystem.getAnimatedRegions();
|
|
2858
|
+
return { animatedRegions: result?.animatedRegions || [], count: result?.count || 0 };
|
|
2859
|
+
} catch (error) {
|
|
2860
|
+
return { success: false, error: error.message };
|
|
2861
|
+
}
|
|
2862
|
+
})();
|
|
2863
|
+
`.trim();
|
|
2864
|
+
}
|
|
2865
|
+
/**
|
|
2866
|
+
* Generate code to export map region data as CSV
|
|
2867
|
+
*/
|
|
2868
|
+
generateExportMapRegionCSV(input) {
|
|
2869
|
+
const validated = ExportMapRegionCSVInputSchema.parse(input);
|
|
2870
|
+
return `
|
|
2871
|
+
// Export map region data as CSV
|
|
2872
|
+
(function() {
|
|
2873
|
+
if (!app.mapSystem) {
|
|
2874
|
+
return { success: false, error: 'Map system not available' };
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
try {
|
|
2878
|
+
const result = app.mapSystem.exportRegionCSV({
|
|
2879
|
+
includeHighlighted: ${validated.includeHighlighted !== false},
|
|
2880
|
+
includeSelected: ${validated.includeSelected !== false},
|
|
2881
|
+
includeColors: ${validated.includeColors !== false},
|
|
2882
|
+
download: ${validated.download === true},
|
|
2883
|
+
filename: '${validated.filename || 'map-regions.csv'}'
|
|
2884
|
+
});
|
|
2885
|
+
return { success: true, csv: result?.csv || '', regionCount: result?.regionCount || 0 };
|
|
2886
|
+
} catch (error) {
|
|
2887
|
+
return { success: false, error: error.message };
|
|
2888
|
+
}
|
|
2889
|
+
})();
|
|
2890
|
+
`.trim();
|
|
2891
|
+
}
|
|
2892
|
+
/**
|
|
2893
|
+
* Generate code to import CSV data to update map regions
|
|
2894
|
+
*/
|
|
2895
|
+
generateImportMapRegionCSV(input) {
|
|
2896
|
+
const validated = ImportMapRegionCSVInputSchema.parse(input);
|
|
2897
|
+
// Escape the CSV text for JavaScript string
|
|
2898
|
+
const escapedCsv = validated.csvText.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
|
2899
|
+
return `
|
|
2900
|
+
// Import CSV data to update map regions
|
|
2901
|
+
(function() {
|
|
2902
|
+
if (!app.mapSystem) {
|
|
2903
|
+
return { success: false, error: 'Map system not available' };
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2906
|
+
try {
|
|
2907
|
+
const result = app.mapSystem.importRegionCSV({
|
|
2908
|
+
csvText: \`${escapedCsv}\`,
|
|
2909
|
+
applyColors: ${validated.applyColors !== false},
|
|
2910
|
+
applyHighlight: ${validated.applyHighlight !== false},
|
|
2911
|
+
applySelection: ${validated.applySelection !== false}
|
|
2912
|
+
});
|
|
2913
|
+
return { success: true, updatedRegions: result?.updatedRegions || 0, notFound: result?.notFound || [], errors: result?.errors || [] };
|
|
2914
|
+
} catch (error) {
|
|
2915
|
+
return { success: false, error: error.message };
|
|
2916
|
+
}
|
|
2917
|
+
})();
|
|
2918
|
+
`.trim();
|
|
2919
|
+
}
|
|
2920
|
+
/**
|
|
2921
|
+
* Generate code to select map regions
|
|
2922
|
+
*/
|
|
2923
|
+
generateSelectMapRegions(input) {
|
|
2924
|
+
const validated = SelectMapRegionsInputSchema.parse(input);
|
|
2925
|
+
const regionIds = JSON.stringify(validated.regionIds);
|
|
2926
|
+
return `
|
|
2927
|
+
// Select map regions
|
|
2928
|
+
(function() {
|
|
2929
|
+
if (!app.mapSystem) {
|
|
2930
|
+
return { success: false, error: 'Map system not available' };
|
|
2931
|
+
}
|
|
2932
|
+
|
|
2933
|
+
try {
|
|
2934
|
+
app.mapSystem.selectRegions(${regionIds});
|
|
2935
|
+
return { success: true, selected: ${regionIds} };
|
|
2936
|
+
} catch (error) {
|
|
2937
|
+
return { success: false, error: error.message };
|
|
2938
|
+
}
|
|
2939
|
+
})();
|
|
2940
|
+
`.trim();
|
|
2941
|
+
}
|
|
2942
|
+
/**
|
|
2943
|
+
* Generate code to deselect map regions
|
|
2944
|
+
*/
|
|
2945
|
+
generateDeselectMapRegions(input) {
|
|
2946
|
+
const validated = DeselectMapRegionsInputSchema.parse(input);
|
|
2947
|
+
const regionIds = validated.regionIds ? JSON.stringify(validated.regionIds) : 'null';
|
|
2948
|
+
return `
|
|
2949
|
+
// Deselect map regions
|
|
2950
|
+
(function() {
|
|
2951
|
+
if (!app.mapSystem) {
|
|
2952
|
+
return { success: false, error: 'Map system not available' };
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
try {
|
|
2956
|
+
app.mapSystem.deselectRegions(${regionIds});
|
|
2957
|
+
return { success: true };
|
|
2958
|
+
} catch (error) {
|
|
2959
|
+
return { success: false, error: error.message };
|
|
2960
|
+
}
|
|
2961
|
+
})();
|
|
2962
|
+
`.trim();
|
|
2963
|
+
}
|
|
2964
|
+
/**
|
|
2965
|
+
* Generate code to get highlighted map regions
|
|
2966
|
+
*/
|
|
2967
|
+
generateGetHighlightedMapRegions() {
|
|
2968
|
+
return `
|
|
2969
|
+
// Get highlighted map regions
|
|
2970
|
+
(function() {
|
|
2971
|
+
if (!app.mapSystem) {
|
|
2972
|
+
return { success: false, error: 'Map system not available' };
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
try {
|
|
2976
|
+
const result = app.mapSystem.getHighlightedRegions();
|
|
2977
|
+
return { highlighted: result?.highlighted || [], count: result?.count || 0 };
|
|
2978
|
+
} catch (error) {
|
|
2979
|
+
return { success: false, error: error.message };
|
|
2980
|
+
}
|
|
2981
|
+
})();
|
|
2982
|
+
`.trim();
|
|
2983
|
+
}
|
|
2984
|
+
/**
|
|
2985
|
+
* Generate code to export the complete scene state
|
|
2986
|
+
*/
|
|
2987
|
+
generateExportScene() {
|
|
2988
|
+
return `
|
|
2989
|
+
// Export complete scene state
|
|
2990
|
+
(function() {
|
|
2991
|
+
try {
|
|
2992
|
+
const items = [];
|
|
2993
|
+
const relations = [];
|
|
2994
|
+
const decorative = [];
|
|
2995
|
+
|
|
2996
|
+
// Collect items from registry
|
|
2997
|
+
if (app.itemRegistry) {
|
|
2998
|
+
for (const [id, item] of app.itemRegistry.entries()) {
|
|
2999
|
+
const itemData = {
|
|
3000
|
+
id: id,
|
|
3001
|
+
type: item.data?.type || item.className?.toLowerCase() || 'unknown',
|
|
3002
|
+
position: item.position ? { x: item.position.x, y: item.position.y } : null,
|
|
3003
|
+
bounds: item.bounds ? {
|
|
3004
|
+
x: item.bounds.x,
|
|
3005
|
+
y: item.bounds.y,
|
|
3006
|
+
width: item.bounds.width,
|
|
3007
|
+
height: item.bounds.height
|
|
3008
|
+
} : null,
|
|
3009
|
+
properties: {}
|
|
3010
|
+
};
|
|
3011
|
+
|
|
3012
|
+
// Collect common properties
|
|
3013
|
+
if (item.fillColor) itemData.properties.fillColor = item.fillColor.toCSS ? item.fillColor.toCSS() : item.fillColor;
|
|
3014
|
+
if (item.strokeColor) itemData.properties.strokeColor = item.strokeColor.toCSS ? item.strokeColor.toCSS() : item.strokeColor;
|
|
3015
|
+
if (item.strokeWidth) itemData.properties.strokeWidth = item.strokeWidth;
|
|
3016
|
+
if (item.opacity !== undefined) itemData.properties.opacity = item.opacity;
|
|
3017
|
+
if (item.rotation) itemData.properties.rotation = item.rotation;
|
|
3018
|
+
if (item.data?.content) itemData.properties.content = item.data.content;
|
|
3019
|
+
|
|
3020
|
+
if (item.data?.isDecorative) {
|
|
3021
|
+
decorative.push(itemData);
|
|
3022
|
+
} else {
|
|
3023
|
+
items.push(itemData);
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
// Collect relations
|
|
3029
|
+
if (app.getRelations) {
|
|
3030
|
+
const activeRelations = app.getRelations();
|
|
3031
|
+
for (const rel of activeRelations) {
|
|
3032
|
+
relations.push({
|
|
3033
|
+
sourceId: rel.sourceId,
|
|
3034
|
+
targetId: rel.targetId,
|
|
3035
|
+
type: rel.type,
|
|
3036
|
+
params: rel.params
|
|
3037
|
+
});
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
|
|
3041
|
+
// Get background color and canvas size
|
|
3042
|
+
const backgroundColor = app.getBackgroundColor ? app.getBackgroundColor() : null;
|
|
3043
|
+
const canvasSize = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 };
|
|
3044
|
+
|
|
3045
|
+
return {
|
|
3046
|
+
success: true,
|
|
3047
|
+
items: items,
|
|
3048
|
+
relations: relations,
|
|
3049
|
+
decorative: decorative,
|
|
3050
|
+
backgroundColor: backgroundColor,
|
|
3051
|
+
canvasSize: canvasSize,
|
|
3052
|
+
itemCount: items.length,
|
|
3053
|
+
relationCount: relations.length
|
|
3054
|
+
};
|
|
3055
|
+
} catch (error) {
|
|
3056
|
+
return { success: false, error: error.message };
|
|
3057
|
+
}
|
|
3058
|
+
})();
|
|
3059
|
+
`.trim();
|
|
3060
|
+
}
|
|
3061
|
+
/**
|
|
3062
|
+
* Generate code to export map as GeoJSON with current styling
|
|
3063
|
+
*/
|
|
3064
|
+
generateExportMapGeoJson(options = {}) {
|
|
3065
|
+
const includeStyles = options.includeStyles !== false;
|
|
3066
|
+
const includeMetadata = options.includeMetadata !== false;
|
|
3067
|
+
const selectedOnly = options.selectedOnly || false;
|
|
3068
|
+
const download = options.download || false;
|
|
3069
|
+
const filename = options.filename || 'map-export.geojson';
|
|
3070
|
+
return `
|
|
3071
|
+
// Export map as GeoJSON
|
|
3072
|
+
(function() {
|
|
3073
|
+
if (!app.mapSystem) {
|
|
3074
|
+
return { success: false, error: 'Map system not available' };
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
try {
|
|
3078
|
+
const result = app.mapSystem.exportGeoJSON({
|
|
3079
|
+
includeStyles: ${includeStyles},
|
|
3080
|
+
includeMetadata: ${includeMetadata},
|
|
3081
|
+
selectedOnly: ${selectedOnly},
|
|
3082
|
+
download: ${download},
|
|
3083
|
+
filename: ${JSON.stringify(filename)}
|
|
3084
|
+
});
|
|
3085
|
+
return result;
|
|
3086
|
+
} catch (error) {
|
|
3087
|
+
return { success: false, error: error.message };
|
|
3088
|
+
}
|
|
3089
|
+
})();
|
|
3090
|
+
`.trim();
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Generate code to export original map GeoJSON (unmodified boundaries)
|
|
3094
|
+
*/
|
|
3095
|
+
generateExportOriginalMapGeoJson(options = {}) {
|
|
3096
|
+
const download = options.download || false;
|
|
3097
|
+
const filename = options.filename || 'map-source.geojson';
|
|
3098
|
+
return `
|
|
3099
|
+
// Export original map GeoJSON
|
|
3100
|
+
(function() {
|
|
3101
|
+
if (!app.mapSystem) {
|
|
3102
|
+
return { success: false, error: 'Map system not available' };
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
try {
|
|
3106
|
+
const result = app.mapSystem.exportOriginalGeoJSON({
|
|
3107
|
+
download: ${download},
|
|
3108
|
+
filename: ${JSON.stringify(filename)}
|
|
3109
|
+
});
|
|
3110
|
+
return result;
|
|
3111
|
+
} catch (error) {
|
|
3112
|
+
return { success: false, error: error.message };
|
|
3113
|
+
}
|
|
3114
|
+
})();
|
|
3115
|
+
`.trim();
|
|
3116
|
+
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Generate code to get map source info
|
|
3119
|
+
*/
|
|
3120
|
+
generateGetMapSourceInfo() {
|
|
3121
|
+
return `
|
|
3122
|
+
// Get map source info
|
|
3123
|
+
(function() {
|
|
3124
|
+
if (!app.mapSystem) {
|
|
3125
|
+
return { success: false, error: 'Map system not available' };
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3128
|
+
try {
|
|
3129
|
+
const result = app.mapSystem.getSourceInfo();
|
|
3130
|
+
return result;
|
|
3131
|
+
} catch (error) {
|
|
3132
|
+
return { success: false, error: error.message };
|
|
3133
|
+
}
|
|
3134
|
+
})();
|
|
3135
|
+
`.trim();
|
|
3136
|
+
}
|
|
3137
|
+
/**
|
|
3138
|
+
* Generate code to register a Paper.js item
|
|
3139
|
+
*/
|
|
3140
|
+
generateRegisterItem(input) {
|
|
3141
|
+
const itemJsonStr = JSON.stringify(input.itemJson);
|
|
3142
|
+
const propertiesStr = input.properties ? JSON.stringify(input.properties) : '{}';
|
|
3143
|
+
return `
|
|
3144
|
+
// Register Paper.js item
|
|
3145
|
+
(function() {
|
|
3146
|
+
try {
|
|
3147
|
+
// Import the Paper.js item from JSON
|
|
3148
|
+
const item = paper.project.importJSON(${itemJsonStr});
|
|
3149
|
+
if (!item) {
|
|
3150
|
+
return { success: false, error: 'Failed to import item from JSON' };
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
// Register in item registry
|
|
3154
|
+
const itemId = app.itemRegistry ? 'item_' + (app.itemRegistry.size + 1) : 'item_1';
|
|
3155
|
+
item.data = item.data || {};
|
|
3156
|
+
item.data.registryId = itemId;
|
|
3157
|
+
item.data.type = ${JSON.stringify(input.itemType)};
|
|
3158
|
+
|
|
3159
|
+
// Merge custom properties
|
|
3160
|
+
const customProps = ${propertiesStr};
|
|
3161
|
+
Object.assign(item.data, customProps);
|
|
3162
|
+
|
|
3163
|
+
// Add to registry
|
|
3164
|
+
if (app.itemRegistry) {
|
|
3165
|
+
app.itemRegistry.set(itemId, item);
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
// Add to appropriate group
|
|
3169
|
+
if (app.textItemGroup) {
|
|
3170
|
+
app.textItemGroup.addChild(item);
|
|
3171
|
+
}
|
|
3172
|
+
|
|
3173
|
+
return {
|
|
3174
|
+
success: true,
|
|
3175
|
+
itemId: itemId,
|
|
3176
|
+
type: ${JSON.stringify(input.itemType)},
|
|
3177
|
+
position: item.position ? { x: item.position.x, y: item.position.y } : null,
|
|
3178
|
+
bounds: item.bounds ? {
|
|
3179
|
+
x: item.bounds.x,
|
|
3180
|
+
y: item.bounds.y,
|
|
3181
|
+
width: item.bounds.width,
|
|
3182
|
+
height: item.bounds.height
|
|
3183
|
+
} : null
|
|
3184
|
+
};
|
|
3185
|
+
} catch (error) {
|
|
3186
|
+
return { success: false, error: error.message };
|
|
3187
|
+
}
|
|
3188
|
+
})();
|
|
3189
|
+
`.trim();
|
|
3190
|
+
}
|
|
3191
|
+
// =============================================================================
|
|
3192
|
+
// CUSTOM RELATION & CODE GENERATORS
|
|
3193
|
+
// =============================================================================
|
|
3194
|
+
generateRegisterCustomRelation(input) {
|
|
3195
|
+
const validated = RegisterCustomRelationInputSchema.parse(input);
|
|
3196
|
+
const paramsStr = validated.params ? JSON.stringify(validated.params) : '{}';
|
|
3197
|
+
const templatesStr = validated.templates ? JSON.stringify(validated.templates) : '[]';
|
|
3198
|
+
return `
|
|
3199
|
+
// Register custom relation: ${validated.name}
|
|
3200
|
+
(function() {
|
|
3201
|
+
try {
|
|
3202
|
+
app.registerRelationRule('${validated.name}', {
|
|
3203
|
+
description: ${JSON.stringify(validated.description || '')},
|
|
3204
|
+
params: ${paramsStr},
|
|
3205
|
+
compute: (ctx) => {
|
|
3206
|
+
const { fromPosition, toPosition, params, delta, time } = ctx;
|
|
3207
|
+
${validated.computeFunction}
|
|
3208
|
+
},
|
|
3209
|
+
apply: (item, target, computed, params) => {
|
|
3210
|
+
${validated.applyFunction}
|
|
3211
|
+
},
|
|
3212
|
+
templates: ${templatesStr},
|
|
3213
|
+
continuous: ${validated.continuous !== false},
|
|
3214
|
+
priority: ${validated.priority || 0}
|
|
3215
|
+
});
|
|
3216
|
+
return { success: true, relationName: '${validated.name}', message: 'Custom relation registered successfully' };
|
|
3217
|
+
} catch (error) {
|
|
3218
|
+
return { success: false, error: error.message };
|
|
3219
|
+
}
|
|
3220
|
+
})();
|
|
3221
|
+
`.trim();
|
|
3222
|
+
}
|
|
3223
|
+
generateExecuteCustomCode(input) {
|
|
3224
|
+
const validated = ExecuteCustomCodeInputSchema.parse(input);
|
|
3225
|
+
const description = validated.description || 'Execute custom code';
|
|
3226
|
+
return `
|
|
3227
|
+
// ${description}
|
|
3228
|
+
(function() {
|
|
3229
|
+
try {
|
|
3230
|
+
${validated.code}
|
|
3231
|
+
} catch (error) {
|
|
3232
|
+
return { success: false, error: error.message };
|
|
3233
|
+
}
|
|
3234
|
+
})();
|
|
3235
|
+
`.trim();
|
|
3236
|
+
}
|
|
3237
|
+
// =============================================================================
|
|
3238
|
+
// MASK GENERATORS
|
|
3239
|
+
// =============================================================================
|
|
3240
|
+
generateApplyAnimatedMask(input) {
|
|
3241
|
+
const validated = ApplyAnimatedMaskInputSchema.parse(input);
|
|
3242
|
+
const optionsStr = validated.options ? JSON.stringify(validated.options) : '{}';
|
|
3243
|
+
const keyframesStr = validated.keyframes ? JSON.stringify(validated.keyframes) : 'null';
|
|
3244
|
+
const maskOptionsStr = validated.maskOptions ? JSON.stringify(validated.maskOptions) : '{}';
|
|
3245
|
+
return `
|
|
3246
|
+
// Apply animated mask to ${validated.itemId}
|
|
3247
|
+
(function() {
|
|
3248
|
+
if (!app.maskSystem) {
|
|
3249
|
+
return { success: false, error: 'Mask system not available' };
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
try {
|
|
3253
|
+
const result = app.maskSystem.applyAnimatedMask('${validated.itemId}', {
|
|
3254
|
+
preset: ${validated.preset ? `'${validated.preset}'` : 'null'},
|
|
3255
|
+
maskType: ${validated.maskType ? `'${validated.maskType}'` : 'null'},
|
|
3256
|
+
keyframes: ${keyframesStr},
|
|
3257
|
+
options: ${optionsStr},
|
|
3258
|
+
maskOptions: ${maskOptionsStr}
|
|
3259
|
+
});
|
|
3260
|
+
return result;
|
|
3261
|
+
} catch (error) {
|
|
3262
|
+
return { success: false, error: error.message };
|
|
3263
|
+
}
|
|
3264
|
+
})();
|
|
3265
|
+
`.trim();
|
|
3266
|
+
}
|
|
3267
|
+
generateApplyCustomMask(input) {
|
|
3268
|
+
const validated = ApplyCustomMaskInputSchema.parse(input);
|
|
3269
|
+
const keyframesStr = JSON.stringify(validated.keyframes);
|
|
3270
|
+
const maskOptionsStr = validated.maskOptions ? JSON.stringify(validated.maskOptions) : '{}';
|
|
3271
|
+
return `
|
|
3272
|
+
// Apply custom mask to ${validated.itemId}
|
|
3273
|
+
(function() {
|
|
3274
|
+
if (!app.maskSystem) {
|
|
3275
|
+
return { success: false, error: 'Mask system not available' };
|
|
3276
|
+
}
|
|
3277
|
+
|
|
3278
|
+
try {
|
|
3279
|
+
const result = app.maskSystem.applyCustomMask('${validated.itemId}', {
|
|
3280
|
+
maskType: '${validated.maskType}',
|
|
3281
|
+
keyframes: ${keyframesStr},
|
|
3282
|
+
maskOptions: ${maskOptionsStr}
|
|
3283
|
+
});
|
|
3284
|
+
return result;
|
|
3285
|
+
} catch (error) {
|
|
3286
|
+
return { success: false, error: error.message };
|
|
3287
|
+
}
|
|
3288
|
+
})();
|
|
3289
|
+
`.trim();
|
|
3290
|
+
}
|
|
3291
|
+
generateRemoveMask(input) {
|
|
3292
|
+
const validated = RemoveMaskInputSchema.parse(input);
|
|
3293
|
+
return `
|
|
3294
|
+
// Remove mask from ${validated.itemId}
|
|
3295
|
+
(function() {
|
|
3296
|
+
if (!app.maskSystem) {
|
|
3297
|
+
return { success: false, error: 'Mask system not available' };
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
try {
|
|
3301
|
+
const result = app.maskSystem.removeMask('${validated.itemId}');
|
|
3302
|
+
return { success: true, itemId: '${validated.itemId}', message: 'Mask removed' };
|
|
3303
|
+
} catch (error) {
|
|
3304
|
+
return { success: false, error: error.message };
|
|
3305
|
+
}
|
|
3306
|
+
})();
|
|
3307
|
+
`.trim();
|
|
3308
|
+
}
|
|
3309
|
+
generateGetAnimatableProperties() {
|
|
3310
|
+
return `
|
|
3311
|
+
// Get animatable properties for mask types
|
|
3312
|
+
(function() {
|
|
3313
|
+
return {
|
|
3314
|
+
rectangle: {
|
|
3315
|
+
properties: ['x', 'y', 'width', 'height', 'rotation', 'opacity'],
|
|
3316
|
+
description: 'Rectangular mask with position and size'
|
|
3317
|
+
},
|
|
3318
|
+
circle: {
|
|
3319
|
+
properties: ['x', 'y', 'radius', 'scale', 'opacity'],
|
|
3320
|
+
description: 'Circular mask with center and radius'
|
|
3321
|
+
},
|
|
3322
|
+
ellipse: {
|
|
3323
|
+
properties: ['x', 'y', 'radiusX', 'radiusY', 'rotation', 'scale', 'opacity'],
|
|
3324
|
+
description: 'Elliptical mask with separate radii'
|
|
3325
|
+
},
|
|
3326
|
+
star: {
|
|
3327
|
+
properties: ['x', 'y', 'radius', 'scale', 'rotation', 'opacity'],
|
|
3328
|
+
description: 'Star mask with configurable points'
|
|
3329
|
+
},
|
|
3330
|
+
triangle: {
|
|
3331
|
+
properties: ['x', 'y', 'scale', 'rotation', 'opacity'],
|
|
3332
|
+
description: 'Triangular mask'
|
|
3333
|
+
},
|
|
3334
|
+
hexagon: {
|
|
3335
|
+
properties: ['x', 'y', 'scale', 'rotation', 'opacity'],
|
|
3336
|
+
description: 'Hexagonal mask'
|
|
3337
|
+
},
|
|
3338
|
+
heart: {
|
|
3339
|
+
properties: ['x', 'y', 'scale', 'rotation', 'opacity'],
|
|
3340
|
+
description: 'Heart-shaped mask'
|
|
3341
|
+
},
|
|
3342
|
+
rounded: {
|
|
3343
|
+
properties: ['x', 'y', 'width', 'height', 'cornerRadius', 'rotation', 'opacity'],
|
|
3344
|
+
description: 'Rounded rectangle mask'
|
|
3345
|
+
}
|
|
3346
|
+
};
|
|
3347
|
+
})();
|
|
3348
|
+
`.trim();
|
|
3349
|
+
}
|
|
3350
|
+
generateGetAvailableEasings() {
|
|
3351
|
+
return `
|
|
3352
|
+
// Get available easing functions
|
|
3353
|
+
(function() {
|
|
3354
|
+
return {
|
|
3355
|
+
easings: ['linear', 'easeIn', 'easeOut', 'easeInOut', 'bounce', 'elastic'],
|
|
3356
|
+
descriptions: {
|
|
3357
|
+
linear: 'Constant speed',
|
|
3358
|
+
easeIn: 'Slow start, fast end',
|
|
3359
|
+
easeOut: 'Fast start, slow end',
|
|
3360
|
+
easeInOut: 'Slow start and end',
|
|
3361
|
+
bounce: 'Bouncing effect at end',
|
|
3362
|
+
elastic: 'Spring-like overshoot'
|
|
3363
|
+
}
|
|
3364
|
+
};
|
|
3365
|
+
})();
|
|
3366
|
+
`.trim();
|
|
3367
|
+
}
|
|
3368
|
+
generateGetMaskTypes() {
|
|
3369
|
+
return `
|
|
3370
|
+
// Get available mask types
|
|
3371
|
+
(function() {
|
|
3372
|
+
return {
|
|
3373
|
+
maskTypes: ['rectangle', 'circle', 'ellipse', 'star', 'triangle', 'hexagon', 'heart', 'rounded', 'custom']
|
|
3374
|
+
};
|
|
3375
|
+
})();
|
|
3376
|
+
`.trim();
|
|
3377
|
+
}
|
|
3378
|
+
generateGetMaskAnimations() {
|
|
3379
|
+
return `
|
|
3380
|
+
// Get available mask animation presets
|
|
3381
|
+
(function() {
|
|
3382
|
+
return {
|
|
3383
|
+
animations: [
|
|
3384
|
+
'wipeLeft', 'wipeRight', 'wipeUp', 'wipeDown',
|
|
3385
|
+
'iris', 'irisOut',
|
|
3386
|
+
'star', 'heart',
|
|
3387
|
+
'curtainHorizontal', 'curtainVertical', 'cinematic',
|
|
3388
|
+
'diagonalWipe',
|
|
3389
|
+
'revealUp', 'revealDown'
|
|
3390
|
+
]
|
|
3391
|
+
};
|
|
3392
|
+
})();
|
|
3393
|
+
`.trim();
|
|
3394
|
+
}
|
|
3395
|
+
generateApplyTemplate(input) {
|
|
3396
|
+
const { templateId, category, listOnly } = input;
|
|
3397
|
+
if (listOnly || !templateId) {
|
|
3398
|
+
// List mode: return available templates, optionally filtered by category
|
|
3399
|
+
const categoryFilter = category ? `'${category}'` : 'null';
|
|
3400
|
+
return `
|
|
3401
|
+
// List available templates
|
|
3402
|
+
(function() {
|
|
3403
|
+
if (!app.templateManager) {
|
|
3404
|
+
return { error: 'Template manager not available. Make sure PinePaper Studio is loaded.' };
|
|
3405
|
+
}
|
|
3406
|
+
const allTemplates = app.templateManager.getAllTemplates();
|
|
3407
|
+
const category = ${categoryFilter};
|
|
3408
|
+
const filtered = category
|
|
3409
|
+
? allTemplates.filter(t => t.category === category)
|
|
3410
|
+
: allTemplates;
|
|
3411
|
+
return {
|
|
3412
|
+
templates: filtered.map(t => ({
|
|
3413
|
+
id: t.id,
|
|
3414
|
+
name: t.name,
|
|
3415
|
+
category: t.category,
|
|
3416
|
+
description: t.description || ''
|
|
3417
|
+
})),
|
|
3418
|
+
count: filtered.length,
|
|
3419
|
+
categories: [...new Set(allTemplates.map(t => t.category))]
|
|
3420
|
+
};
|
|
3421
|
+
})();
|
|
3422
|
+
`.trim();
|
|
3423
|
+
}
|
|
3424
|
+
// Load mode: apply a specific template
|
|
3425
|
+
return `
|
|
3426
|
+
// Apply template: ${templateId}
|
|
3427
|
+
(async function() {
|
|
3428
|
+
if (!app.templateManager) {
|
|
3429
|
+
return { error: 'Template manager not available. Make sure PinePaper Studio is loaded.' };
|
|
3430
|
+
}
|
|
3431
|
+
try {
|
|
3432
|
+
await app.templateManager.loadTemplate('${templateId}', true);
|
|
3433
|
+
return { success: true, templateId: '${templateId}', message: 'Template applied successfully. Canvas has been replaced with template content.' };
|
|
3434
|
+
} catch (e) {
|
|
3435
|
+
return { error: 'Failed to apply template: ' + e.message };
|
|
3436
|
+
}
|
|
3437
|
+
})();
|
|
3438
|
+
`.trim();
|
|
3439
|
+
}
|
|
3440
|
+
generateImportImage(input) {
|
|
3441
|
+
const { url, position, maxWidth, maxHeight, mask } = input;
|
|
3442
|
+
const positionOpts = position ? `, position: { x: ${position.x}, y: ${position.y} }` : '';
|
|
3443
|
+
const maxWidthOpt = maxWidth !== undefined ? `, maxWidth: ${maxWidth}` : '';
|
|
3444
|
+
const maxHeightOpt = maxHeight !== undefined ? `, maxHeight: ${maxHeight}` : '';
|
|
3445
|
+
return `
|
|
3446
|
+
// Import image from URL
|
|
3447
|
+
(async function() {
|
|
3448
|
+
if (!app.imageTools) {
|
|
3449
|
+
return { error: 'Image tools not available. Make sure PinePaper Studio is loaded.' };
|
|
3450
|
+
}
|
|
3451
|
+
try {
|
|
3452
|
+
const entry = await app.imageTools.uploadFromURL('${url}');
|
|
3453
|
+
const opts = {${positionOpts}${maxWidthOpt}${maxHeightOpt} };
|
|
3454
|
+
const raster = await app.imageTools.placeImage(entry.id, Object.keys(opts).length > 0 ? opts : undefined);
|
|
3455
|
+
${mask ? ` app.imageTools.applyMask(raster, '${mask}');\n` : ''} const itemId = raster.data?.itemId || raster.name || raster.id;
|
|
3456
|
+
return {
|
|
3457
|
+
success: true,
|
|
3458
|
+
itemId: itemId,
|
|
3459
|
+
message: 'Image imported and placed on canvas.',
|
|
3460
|
+
bounds: { x: raster.bounds.x, y: raster.bounds.y, width: raster.bounds.width, height: raster.bounds.height }${mask ? `,\n mask: '${mask}'` : ''}
|
|
3461
|
+
};
|
|
3462
|
+
} catch (e) {
|
|
3463
|
+
return { error: 'Failed to import image: ' + e.message };
|
|
3464
|
+
}
|
|
3465
|
+
})();
|
|
3466
|
+
`.trim();
|
|
3467
|
+
}
|
|
1327
3468
|
}
|
|
1328
3469
|
// Export singleton instance
|
|
1329
3470
|
export const codeGenerator = new PinePaperCodeGenerator();
|