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