@pinepaper.studio/mcp-server 1.5.1 → 1.5.4
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 +6 -4
- package/dist/agent/types.d.ts +16 -16
- package/dist/browser/puppeteer-controller.d.ts +5 -1
- package/dist/browser/puppeteer-controller.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.js +11 -3
- package/dist/browser/puppeteer-controller.js.map +1 -1
- package/dist/browser/url-target.d.ts +26 -0
- package/dist/browser/url-target.d.ts.map +1 -0
- package/dist/browser/url-target.js +50 -0
- package/dist/browser/url-target.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +128 -109
- package/dist/index.js.map +1 -1
- package/dist/ontology/design-graph.d.ts +85 -0
- package/dist/ontology/design-graph.d.ts.map +1 -0
- package/dist/ontology/design-graph.js +944 -0
- package/dist/ontology/design-graph.js.map +1 -0
- package/dist/ontology/hints.d.ts +71 -0
- package/dist/ontology/hints.d.ts.map +1 -0
- package/dist/ontology/hints.js +189 -0
- package/dist/ontology/hints.js.map +1 -0
- package/dist/ontology/index.d.ts +11 -0
- package/dist/ontology/index.d.ts.map +1 -0
- package/dist/ontology/index.js +14 -0
- package/dist/ontology/index.js.map +1 -0
- package/dist/ontology/semantic-vocabulary.d.ts +12 -0
- package/dist/ontology/semantic-vocabulary.d.ts.map +1 -0
- package/dist/ontology/semantic-vocabulary.js +26 -0
- package/dist/ontology/semantic-vocabulary.js.map +1 -0
- package/dist/ontology/types.d.ts +255 -0
- package/dist/ontology/types.d.ts.map +1 -0
- package/dist/ontology/types.js +10 -0
- package/dist/ontology/types.js.map +1 -0
- package/dist/ontology/validator.d.ts +36 -0
- package/dist/ontology/validator.d.ts.map +1 -0
- package/dist/ontology/validator.js +374 -0
- package/dist/ontology/validator.js.map +1 -0
- package/dist/ontology/vocabulary.d.ts +34 -0
- package/dist/ontology/vocabulary.d.ts.map +1 -0
- package/dist/ontology/vocabulary.js +429 -0
- package/dist/ontology/vocabulary.js.map +1 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +146 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/tools/compact-descriptions.d.ts +5 -2
- package/dist/tools/compact-descriptions.d.ts.map +1 -1
- package/dist/tools/compact-descriptions.js +56 -235
- package/dist/tools/compact-descriptions.js.map +1 -1
- package/dist/tools/definitions.d.ts +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +1842 -1784
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers/camera.d.ts +19 -0
- package/dist/tools/handlers/camera.d.ts.map +1 -0
- package/dist/tools/handlers/camera.js +94 -0
- package/dist/tools/handlers/camera.js.map +1 -0
- package/dist/tools/handlers/export.d.ts +12 -0
- package/dist/tools/handlers/export.d.ts.map +1 -0
- package/dist/tools/handlers/export.js +30 -0
- package/dist/tools/handlers/export.js.map +1 -0
- package/dist/tools/handlers/font.d.ts +12 -0
- package/dist/tools/handlers/font.d.ts.map +1 -0
- package/dist/tools/handlers/font.js +135 -0
- package/dist/tools/handlers/font.js.map +1 -0
- package/dist/tools/handlers/maps.d.ts +19 -0
- package/dist/tools/handlers/maps.d.ts.map +1 -0
- package/dist/tools/handlers/maps.js +184 -0
- package/dist/tools/handlers/maps.js.map +1 -0
- package/dist/tools/handlers/ontology.d.ts +20 -0
- package/dist/tools/handlers/ontology.d.ts.map +1 -0
- package/dist/tools/handlers/ontology.js +185 -0
- package/dist/tools/handlers/ontology.js.map +1 -0
- package/dist/tools/handlers/tool-guide.d.ts +14 -0
- package/dist/tools/handlers/tool-guide.d.ts.map +1 -0
- package/dist/tools/handlers/tool-guide.js +52 -0
- package/dist/tools/handlers/tool-guide.js.map +1 -0
- package/dist/tools/handlers.d.ts +18 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +321 -574
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts +4 -0
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +65 -54
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +67 -46
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +49 -2
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +1456 -39
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +1381 -404
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +426 -20
- package/dist/types/schemas.js.map +1 -1
- package/package.json +2 -2
|
@@ -82,7 +82,7 @@ function generateBlendModeCode(itemVarName, blendMode) {
|
|
|
82
82
|
*/
|
|
83
83
|
function generateCreateItemCode(itemType, position, properties) {
|
|
84
84
|
// Extract special properties that need separate handling
|
|
85
|
-
const { color, fillColor, strokeColor, shadowColor, shadowBlur, shadowOffset, blendMode, opacity, ...baseProperties } = properties;
|
|
85
|
+
const { color, fillColor, strokeColor, shadowColor, shadowBlur, shadowOffset, blendMode, opacity, contentType, contentFormat, countdownTarget, countdownEndText, ...baseProperties } = properties;
|
|
86
86
|
const params = {
|
|
87
87
|
x: position.x,
|
|
88
88
|
y: position.y,
|
|
@@ -127,6 +127,17 @@ const item = app.create('${itemType}', ${JSON.stringify(params, null, 2)});`;
|
|
|
127
127
|
}
|
|
128
128
|
// Ensure item is visible above backgrounds/generators
|
|
129
129
|
code += `\nif (item.bringToFront) item.bringToFront();`;
|
|
130
|
+
// Add dynamic content support for text items
|
|
131
|
+
if (contentType && itemType === 'text') {
|
|
132
|
+
const opts = {};
|
|
133
|
+
if (contentFormat)
|
|
134
|
+
opts.format = contentFormat;
|
|
135
|
+
if (countdownTarget !== undefined)
|
|
136
|
+
opts.countdownTarget = countdownTarget;
|
|
137
|
+
if (countdownEndText)
|
|
138
|
+
opts.countdownEndText = countdownEndText;
|
|
139
|
+
code += `\nif (app.setDynamicContent) app.setDynamicContent(item, '${contentType}', ${JSON.stringify(opts)});`;
|
|
140
|
+
}
|
|
130
141
|
code += `
|
|
131
142
|
const itemId = item.data.registryId;
|
|
132
143
|
app.historyManager.saveState();
|
|
@@ -139,15 +150,42 @@ app.historyManager.saveState();
|
|
|
139
150
|
* Template for modifying items
|
|
140
151
|
*/
|
|
141
152
|
function generateModifyItemCode(itemId, properties) {
|
|
142
|
-
|
|
153
|
+
const { contentType, contentFormat, countdownTarget, countdownEndText, ...restProperties } = properties;
|
|
154
|
+
let code = `
|
|
143
155
|
// Modify item ${itemId}
|
|
144
|
-
app.select('${itemId}')
|
|
145
|
-
app.modify
|
|
156
|
+
app.select('${itemId}');`;
|
|
157
|
+
// Pass non-dynamic-content properties to app.modify
|
|
158
|
+
if (Object.keys(restProperties).length > 0) {
|
|
159
|
+
code += `\napp.modify(${JSON.stringify(restProperties, null, 2)});`;
|
|
160
|
+
}
|
|
161
|
+
// Handle dynamic content type changes
|
|
162
|
+
if (contentType !== undefined) {
|
|
163
|
+
if (contentType === null || contentType === 'none' || contentType === '') {
|
|
164
|
+
// Remove dynamic content
|
|
165
|
+
code += `
|
|
166
|
+
const entry = app.itemRegistry.get('${itemId}');
|
|
167
|
+
if (entry && entry.item && app.removeDynamicContent) app.removeDynamicContent(entry.item);`;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
// Set dynamic content
|
|
171
|
+
const opts = {};
|
|
172
|
+
if (contentFormat)
|
|
173
|
+
opts.format = contentFormat;
|
|
174
|
+
if (countdownTarget !== undefined)
|
|
175
|
+
opts.countdownTarget = countdownTarget;
|
|
176
|
+
if (countdownEndText)
|
|
177
|
+
opts.countdownEndText = countdownEndText;
|
|
178
|
+
code += `
|
|
179
|
+
const entry = app.itemRegistry.get('${itemId}');
|
|
180
|
+
if (entry && entry.item && app.setDynamicContent) app.setDynamicContent(entry.item, '${contentType}', ${JSON.stringify(opts)});`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
code += `
|
|
146
184
|
app.historyManager.saveState();
|
|
147
185
|
|
|
148
186
|
// Return success
|
|
149
|
-
({ success: true, itemId: '${itemId}' })
|
|
150
|
-
|
|
187
|
+
({ success: true, itemId: '${itemId}' });`;
|
|
188
|
+
return code.trim();
|
|
151
189
|
}
|
|
152
190
|
/**
|
|
153
191
|
* Template for deleting items
|
|
@@ -257,14 +295,21 @@ app.animate(item, {
|
|
|
257
295
|
/**
|
|
258
296
|
* Template for keyframe animation
|
|
259
297
|
*/
|
|
260
|
-
function generateKeyframeAnimateCode(itemId, keyframes, duration, loop = false) {
|
|
298
|
+
function generateKeyframeAnimateCode(itemId, keyframes, duration, loop = false, timeOffset, clipInPoint, clipOutPoint) {
|
|
261
299
|
const keyframesJson = JSON.stringify(keyframes, null, 2);
|
|
262
300
|
const calculatedDuration = duration || Math.max(...keyframes.map(k => k.time));
|
|
301
|
+
const opts = { duration: calculatedDuration, loop };
|
|
302
|
+
if (timeOffset !== undefined)
|
|
303
|
+
opts.timeOffset = timeOffset;
|
|
304
|
+
if (clipInPoint !== undefined)
|
|
305
|
+
opts.clipInPoint = clipInPoint;
|
|
306
|
+
if (clipOutPoint !== undefined)
|
|
307
|
+
opts.clipOutPoint = clipOutPoint;
|
|
263
308
|
return `
|
|
264
309
|
// Apply keyframe animation to ${itemId}
|
|
265
|
-
app.addAnimation('${itemId}', ${keyframesJson},
|
|
310
|
+
app.addAnimation('${itemId}', ${keyframesJson}, ${JSON.stringify(opts)});
|
|
266
311
|
|
|
267
|
-
({ success: true, itemId: '${itemId}', duration: ${calculatedDuration}, loop: ${loop} });
|
|
312
|
+
({ success: true, itemId: '${itemId}', duration: ${calculatedDuration}, loop: ${loop}${timeOffset !== undefined ? `, timeOffset: ${timeOffset}` : ''}${clipInPoint !== undefined ? `, clipInPoint: ${clipInPoint}` : ''}${clipOutPoint !== undefined ? `, clipOutPoint: ${clipOutPoint}` : ''} });
|
|
268
313
|
`.trim();
|
|
269
314
|
}
|
|
270
315
|
/**
|
|
@@ -351,6 +396,12 @@ function generatePlayTimelineCode(action, duration, loop, time) {
|
|
|
351
396
|
// Play keyframe timeline
|
|
352
397
|
app.playKeyframeTimeline(${duration || 5}, ${loop ?? false});
|
|
353
398
|
({ success: true, action: 'play', duration: ${duration || 5}, loop: ${loop ?? false} });
|
|
399
|
+
`.trim();
|
|
400
|
+
case 'pause':
|
|
401
|
+
return `
|
|
402
|
+
// Pause keyframe timeline
|
|
403
|
+
app.pauseKeyframeTimeline();
|
|
404
|
+
({ success: true, action: 'pause' });
|
|
354
405
|
`.trim();
|
|
355
406
|
case 'stop':
|
|
356
407
|
return `
|
|
@@ -576,7 +627,14 @@ export class PinePaperCodeGenerator {
|
|
|
576
627
|
*/
|
|
577
628
|
generateCreateItem(input) {
|
|
578
629
|
const validated = CreateItemInputSchema.parse(input);
|
|
579
|
-
|
|
630
|
+
const properties = { ...validated.properties };
|
|
631
|
+
if (validated.animationType !== undefined)
|
|
632
|
+
properties.animationType = validated.animationType;
|
|
633
|
+
if (validated.animationSpeed !== undefined)
|
|
634
|
+
properties.animationSpeed = validated.animationSpeed;
|
|
635
|
+
if (validated.keyframes !== undefined)
|
|
636
|
+
properties.keyframes = validated.keyframes;
|
|
637
|
+
return generateCreateItemCode(validated.itemType, validated.position, properties);
|
|
580
638
|
}
|
|
581
639
|
/**
|
|
582
640
|
* Generate code for modifying an item
|
|
@@ -622,7 +680,7 @@ export class PinePaperCodeGenerator {
|
|
|
622
680
|
*/
|
|
623
681
|
generateKeyframeAnimate(input) {
|
|
624
682
|
const validated = KeyframeAnimateInputSchema.parse(input);
|
|
625
|
-
return generateKeyframeAnimateCode(validated.itemId, validated.keyframes, validated.duration, validated.loop);
|
|
683
|
+
return generateKeyframeAnimateCode(validated.itemId, validated.keyframes, validated.duration, validated.loop, validated.timeOffset, validated.clipInPoint, validated.clipOutPoint);
|
|
626
684
|
}
|
|
627
685
|
/**
|
|
628
686
|
* Generate code for executing a generator
|
|
@@ -742,6 +800,70 @@ if (imported) {
|
|
|
742
800
|
return `
|
|
743
801
|
// Error: No SVG source provided
|
|
744
802
|
throw new Error('Either svgString or url must be provided');
|
|
803
|
+
`.trim();
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Generate code for importing a Mermaid diagram (flowchart, stateDiagram,
|
|
807
|
+
* sequenceDiagram, erDiagram, classDiagram).
|
|
808
|
+
*/
|
|
809
|
+
generateImportMermaid(mermaidText, options = {}) {
|
|
810
|
+
const escaped = mermaidText.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
|
|
811
|
+
const optsJson = JSON.stringify(options);
|
|
812
|
+
return `
|
|
813
|
+
// Import Mermaid diagram
|
|
814
|
+
(function() {
|
|
815
|
+
if (!app.importMermaid) return { success: false, error: 'importMermaid not available — diagramSystem missing' };
|
|
816
|
+
const mermaidText = \`${escaped}\`;
|
|
817
|
+
const result = app.importMermaid(mermaidText, ${optsJson});
|
|
818
|
+
return {
|
|
819
|
+
success: !!result.success,
|
|
820
|
+
nodeCount: result.nodes ? result.nodes.length : 0,
|
|
821
|
+
edgeCount: result.edges ? result.edges.length : 0,
|
|
822
|
+
nodes: result.nodes || [],
|
|
823
|
+
edges: result.edges || [],
|
|
824
|
+
errors: result.errors || [],
|
|
825
|
+
};
|
|
826
|
+
})();
|
|
827
|
+
`.trim();
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* Generate code for capturing the canvas as pp: ontology. Wraps FxTool's
|
|
831
|
+
* app.exportCanvasOntology() — defined in PinePaper.js after the
|
|
832
|
+
* canvas-state-to-ontology promotion. Output mirrors the cloud-sync
|
|
833
|
+
* postMessage contract: { canvasWidth, canvasHeight, itemCount,
|
|
834
|
+
* itemTypes, hasAnimations, relationCount, items, triples, [viewport] }.
|
|
835
|
+
*
|
|
836
|
+
* Failure modes: app missing the method → return { success: false, error }
|
|
837
|
+
* with a clear FxTool-version hint. Internal exceptions propagate from
|
|
838
|
+
* the browser-side function (TripleLanguage warns to console; we don't
|
|
839
|
+
* fail the tool call on triple-only failures).
|
|
840
|
+
*/
|
|
841
|
+
generateGetCanvasOntology(options = {}) {
|
|
842
|
+
const optsJson = JSON.stringify(options);
|
|
843
|
+
return `
|
|
844
|
+
// Capture canvas as pp: ontology
|
|
845
|
+
(function() {
|
|
846
|
+
if (!app.exportCanvasOntology) {
|
|
847
|
+
return {
|
|
848
|
+
success: false,
|
|
849
|
+
error: 'app.exportCanvasOntology not available — requires FxTool with canvas-ontology promotion. Falling back to pinepaper_get_items is acceptable but loses the triples representation.',
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
const ctx = app.exportCanvasOntology(${optsJson});
|
|
853
|
+
return {
|
|
854
|
+
success: true,
|
|
855
|
+
canvasWidth: ctx.canvasWidth,
|
|
856
|
+
canvasHeight: ctx.canvasHeight,
|
|
857
|
+
canvasPreset: ctx.canvasPreset,
|
|
858
|
+
itemCount: ctx.itemCount,
|
|
859
|
+
itemTypes: ctx.itemTypes,
|
|
860
|
+
hasAnimations: ctx.hasAnimations,
|
|
861
|
+
relationCount: ctx.relationCount,
|
|
862
|
+
items: ctx.items,
|
|
863
|
+
triples: ctx.triples,
|
|
864
|
+
viewport: ctx.viewport,
|
|
865
|
+
};
|
|
866
|
+
})();
|
|
745
867
|
`.trim();
|
|
746
868
|
}
|
|
747
869
|
/**
|
|
@@ -1340,10 +1462,11 @@ throw new Error('Unknown diagram mode action: ${action}');
|
|
|
1340
1462
|
*/
|
|
1341
1463
|
generateAgentStartJob(input) {
|
|
1342
1464
|
const validated = AgentStartJobInputSchema.parse(input);
|
|
1343
|
-
const { name, screenshotPolicy, canvasPreset, clearCanvas } = validated;
|
|
1465
|
+
const { name, screenshotPolicy, canvasPreset, clearCanvas, includeOntology } = validated;
|
|
1344
1466
|
const nameStr = name ? `'${name.replace(/'/g, "\\'")}'` : 'null';
|
|
1345
1467
|
const policyStr = screenshotPolicy || 'on_complete';
|
|
1346
1468
|
const shouldClear = clearCanvas !== false;
|
|
1469
|
+
const wantOntology = includeOntology !== false;
|
|
1347
1470
|
let code = `
|
|
1348
1471
|
// Start agent job
|
|
1349
1472
|
(async function() {
|
|
@@ -1372,10 +1495,31 @@ throw new Error('Unknown diagram mode action: ${action}');
|
|
|
1372
1495
|
app.setCanvasSize('${canvasPreset}');
|
|
1373
1496
|
// Allow canvas resize to take effect
|
|
1374
1497
|
await new Promise(r => setTimeout(r, 50));
|
|
1498
|
+
`;
|
|
1499
|
+
}
|
|
1500
|
+
if (wantOntology) {
|
|
1501
|
+
code += `
|
|
1502
|
+
// Capture initial canvas ontology so the agent has baseline context
|
|
1503
|
+
// without a follow-up pinepaper_get_canvas_ontology call. After
|
|
1504
|
+
// clearCanvas this is a tiny empty-state payload; with clearCanvas:false
|
|
1505
|
+
// it captures whatever's already on the canvas. Older FxTool builds
|
|
1506
|
+
// without exportCanvasOntology produce an _ontologyUnavailable: true
|
|
1507
|
+
// marker so the agent knows to fall back to pinepaper_get_items.
|
|
1508
|
+
let _ontology = null;
|
|
1509
|
+
let _ontologyUnavailable = false;
|
|
1510
|
+
try {
|
|
1511
|
+
if (app.exportCanvasOntology) {
|
|
1512
|
+
_ontology = app.exportCanvasOntology();
|
|
1513
|
+
} else {
|
|
1514
|
+
_ontologyUnavailable = true;
|
|
1515
|
+
}
|
|
1516
|
+
} catch (e) {
|
|
1517
|
+
_ontologyUnavailable = true;
|
|
1518
|
+
}
|
|
1375
1519
|
`;
|
|
1376
1520
|
}
|
|
1377
1521
|
code += `
|
|
1378
|
-
// Return job context with canvas size
|
|
1522
|
+
// Return job context with canvas size${wantOntology ? ' + initial ontology' : ''}
|
|
1379
1523
|
const _cs = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 };
|
|
1380
1524
|
return {
|
|
1381
1525
|
success: true,
|
|
@@ -1384,7 +1528,9 @@ throw new Error('Unknown diagram mode action: ${action}');
|
|
|
1384
1528
|
screenshotPolicy: '${policyStr}',
|
|
1385
1529
|
canvasPreset: ${canvasPreset ? `'${canvasPreset}'` : 'null'},
|
|
1386
1530
|
canvasCleared: ${shouldClear},
|
|
1387
|
-
canvasSize: { width: _cs.width || 800, height: _cs.height || 600 }
|
|
1531
|
+
canvasSize: { width: _cs.width || 800, height: _cs.height || 600 }${wantOntology ? `,
|
|
1532
|
+
ontology: _ontology,
|
|
1533
|
+
ontologyUnavailable: _ontologyUnavailable || undefined` : ''}
|
|
1388
1534
|
};
|
|
1389
1535
|
})();
|
|
1390
1536
|
`;
|
|
@@ -1794,13 +1940,13 @@ return { success: true, action: 'seek', time: ${op.time || 0} };
|
|
|
1794
1940
|
*/
|
|
1795
1941
|
generateAgentExport(input) {
|
|
1796
1942
|
const validated = AgentExportInputSchema.parse(input);
|
|
1797
|
-
const { platform, format, quality } = validated;
|
|
1943
|
+
const { platform, format, quality, framing } = validated;
|
|
1798
1944
|
const qualityLevel = quality || 'standard';
|
|
1799
|
-
// Quality settings
|
|
1945
|
+
// Quality settings (bitrate in bps for VideoEncoder)
|
|
1800
1946
|
const qualitySettings = {
|
|
1801
|
-
draft: { compression: 0.6, fps: 15, dpi: 72 },
|
|
1802
|
-
standard: { compression: 0.85, fps: 30, dpi: 150 },
|
|
1803
|
-
high: { compression: 0.95, fps: 60, dpi: 300 },
|
|
1947
|
+
draft: { compression: 0.6, fps: 15, dpi: 72, bitrate: 2_000_000 },
|
|
1948
|
+
standard: { compression: 0.85, fps: 30, dpi: 150, bitrate: 5_000_000 },
|
|
1949
|
+
high: { compression: 0.95, fps: 60, dpi: 300, bitrate: 8_000_000 },
|
|
1804
1950
|
}[qualityLevel];
|
|
1805
1951
|
// Platform presets
|
|
1806
1952
|
const platformPresets = {
|
|
@@ -1824,10 +1970,38 @@ return { success: true, action: 'seek', time: ${op.time || 0} };
|
|
|
1824
1970
|
const platform = '${platform}';
|
|
1825
1971
|
const format = '${exportFormat}';
|
|
1826
1972
|
const quality = '${qualityLevel}';
|
|
1973
|
+
const framing = '${framing}';
|
|
1827
1974
|
const settings = ${JSON.stringify(qualitySettings)};
|
|
1828
1975
|
const dimensions = ${JSON.stringify({ width: preset.width, height: preset.height })};
|
|
1829
1976
|
|
|
1830
|
-
|
|
1977
|
+
// Resolve camera-view framing dims when requested. Reads the first
|
|
1978
|
+
// camera_animates keyframe's zoom and divides the canvas dims by it
|
|
1979
|
+
// (mirrors FxTool ExportEngine.js:466-485). Camera animation still
|
|
1980
|
+
// drives motion during export — framing only fixes the output frame.
|
|
1981
|
+
let cameraDims = null;
|
|
1982
|
+
if (framing === 'camera') {
|
|
1983
|
+
if (!['gif', 'mp4', 'webm'].includes(format)) {
|
|
1984
|
+
return { success: false, platform, format, framing, error: 'framing: "camera" is only supported for video formats (gif, mp4, webm). Use format: "mp4" / "gif" / "webm" or omit framing.' };
|
|
1985
|
+
}
|
|
1986
|
+
const rr = app.relationRegistry;
|
|
1987
|
+
const hasWalkthrough = rr && typeof rr.hasCameraAnimation === 'function' && rr.hasCameraAnimation();
|
|
1988
|
+
if (!hasWalkthrough) {
|
|
1989
|
+
return { success: false, platform, format, framing, error: 'framing: "camera" requires a camera_animates walkthrough on the canvas. Add one or omit framing.' };
|
|
1990
|
+
}
|
|
1991
|
+
const params = rr.getCameraAnimationParams ? rr.getCameraAnimationParams() : null;
|
|
1992
|
+
if (!params || (params.mode || 'keyframes') !== 'keyframes' || !params.keyframes || !params.keyframes.length) {
|
|
1993
|
+
return { success: false, platform, format, framing, error: 'framing: "camera" requires a keyframe-mode camera walkthrough with at least one keyframe.' };
|
|
1994
|
+
}
|
|
1995
|
+
const firstKf = params.keyframes[0];
|
|
1996
|
+
const firstZoom = firstKf && firstKf.zoom > 0 ? firstKf.zoom : 1;
|
|
1997
|
+
const canvasEl = document.querySelector('canvas');
|
|
1998
|
+
cameraDims = {
|
|
1999
|
+
width: Math.round((canvasEl ? canvasEl.width : dimensions.width) / firstZoom),
|
|
2000
|
+
height: Math.round((canvasEl ? canvasEl.height : dimensions.height) / firstZoom),
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
let result = { success: false, platform, format, quality, framing };
|
|
1831
2005
|
|
|
1832
2006
|
try {
|
|
1833
2007
|
switch (format) {
|
|
@@ -1875,28 +2049,44 @@ return { success: true, action: 'seek', time: ${op.time || 0} };
|
|
|
1875
2049
|
case 'mp4':
|
|
1876
2050
|
case 'webm': {
|
|
1877
2051
|
const videoMimeType = { mp4: 'video/mp4', webm: 'video/webm', gif: 'image/gif' }[format];
|
|
1878
|
-
//
|
|
1879
|
-
|
|
1880
|
-
|
|
2052
|
+
// FxTool VideoExporter._calculateBitrate multiplies a per-resolution
|
|
2053
|
+
// base by settings.quality (0.5-1.0). Our existing per-tier compression
|
|
2054
|
+
// values (0.6 / 0.85 / 0.95) map directly to that scale. Without this,
|
|
2055
|
+
// ExportEngine._quickExportVideo overrides quality with undefined for
|
|
2056
|
+
// mp4/webm, _calculateBitrate yields NaN, and VideoEncoder.configure
|
|
2057
|
+
// rejects with 'malformed bitrate value'.
|
|
2058
|
+
const baseVideoSettings = { format, fps: settings.fps, quality: settings.compression, duration: 5 };
|
|
2059
|
+
const blobToDataUrl = (b) => new Promise(resolve => {
|
|
2060
|
+
const r = new FileReader();
|
|
2061
|
+
r.onloadend = () => resolve(r.result);
|
|
2062
|
+
r.readAsDataURL(b);
|
|
2063
|
+
});
|
|
2064
|
+
// Camera framing requires going direct to videoExporter so width/height
|
|
2065
|
+
// pass through — _quickExportVideo strips dim fields.
|
|
2066
|
+
if (cameraDims && app.exportEngine && app.exportEngine.videoExporter) {
|
|
2067
|
+
const blob = await app.exportEngine.videoExporter.export({ ...baseVideoSettings, width: cameraDims.width, height: cameraDims.height });
|
|
2068
|
+
const dataUrl = await blobToDataUrl(blob);
|
|
2069
|
+
result = { success: true, platform, format, framing, data: dataUrl, mimeType: videoMimeType, size: blob.size, dimensions: cameraDims };
|
|
2070
|
+
} else if ((format === 'mp4' || format === 'webm') && app.exportEngine && app.exportEngine.videoExporter) {
|
|
2071
|
+
// Bypass _quickExportVideo for mp4/webm — it hardcodes
|
|
2072
|
+
// quality:undefined for these formats and produces a NaN bitrate.
|
|
2073
|
+
const blob = await app.exportEngine.videoExporter.export(baseVideoSettings);
|
|
2074
|
+
const dataUrl = await blobToDataUrl(blob);
|
|
2075
|
+
result = { success: true, platform, format, framing, data: dataUrl, mimeType: videoMimeType, size: blob.size };
|
|
2076
|
+
} else if (app.exportEngine && app.exportEngine._quickExportVideo) {
|
|
2077
|
+
// GIF path (working): _quickExportVideo forwards gifQuality to
|
|
2078
|
+
// gif.js. Also serves as the fallback if videoExporter is absent.
|
|
2079
|
+
const videoResult = await app.exportEngine._quickExportVideo(format, baseVideoSettings, false);
|
|
1881
2080
|
if (videoResult && videoResult.blob) {
|
|
1882
|
-
const
|
|
1883
|
-
|
|
1884
|
-
reader.onloadend = () => resolve(reader.result);
|
|
1885
|
-
reader.readAsDataURL(videoResult.blob);
|
|
1886
|
-
});
|
|
1887
|
-
result = { success: true, platform, format, data: dataUrl, mimeType: videoMimeType, size: videoResult.blob.size };
|
|
2081
|
+
const dataUrl = await blobToDataUrl(videoResult.blob);
|
|
2082
|
+
result = { success: true, platform, format, framing, data: dataUrl, mimeType: videoMimeType, size: videoResult.blob.size };
|
|
1888
2083
|
} else {
|
|
1889
2084
|
result = { success: false, error: format.toUpperCase() + ' export returned no data' };
|
|
1890
2085
|
}
|
|
1891
2086
|
} else if (app.exportEngine && app.exportEngine.videoExporter) {
|
|
1892
|
-
|
|
1893
|
-
const
|
|
1894
|
-
|
|
1895
|
-
const dataUrl = await new Promise(resolve => {
|
|
1896
|
-
reader.onloadend = () => resolve(reader.result);
|
|
1897
|
-
reader.readAsDataURL(blob);
|
|
1898
|
-
});
|
|
1899
|
-
result = { success: true, platform, format, data: dataUrl, mimeType: videoMimeType, size: blob.size };
|
|
2087
|
+
const blob = await app.exportEngine.videoExporter.export(baseVideoSettings);
|
|
2088
|
+
const dataUrl = await blobToDataUrl(blob);
|
|
2089
|
+
result = { success: true, platform, format, framing, data: dataUrl, mimeType: videoMimeType, size: blob.size };
|
|
1900
2090
|
} else {
|
|
1901
2091
|
result = { success: false, error: format.toUpperCase() + ' export not available' };
|
|
1902
2092
|
}
|
|
@@ -2339,7 +2529,7 @@ app.historyManager.saveState();
|
|
|
2339
2529
|
if (gradientDirection)
|
|
2340
2530
|
options.gradientDirection = gradientDirection;
|
|
2341
2531
|
return `
|
|
2342
|
-
// Create letter collage
|
|
2532
|
+
// Create letter collage
|
|
2343
2533
|
(function() {
|
|
2344
2534
|
const text = ${JSON.stringify(text)};
|
|
2345
2535
|
const options = ${JSON.stringify(options, null, 2)};
|
|
@@ -2354,7 +2544,21 @@ app.historyManager.saveState();
|
|
|
2354
2544
|
app.historyManager.saveState('create_letter_collage');
|
|
2355
2545
|
}
|
|
2356
2546
|
|
|
2357
|
-
|
|
2547
|
+
// Return a SERIALIZABLE summary — never the raw result. result.group and
|
|
2548
|
+
// result.letters are live Paper.js objects (circular parent/children refs,
|
|
2549
|
+
// methods, canvas handles); the browser round-trip (page.evaluate / CDP
|
|
2550
|
+
// return-by-value) can't serialize them, so returning result throws
|
|
2551
|
+
// "Failed to execute code in browser" even though the collage was already
|
|
2552
|
+
// created — a false negative. Surface registry ids instead.
|
|
2553
|
+
const collageId = (result && (result.collageId
|
|
2554
|
+
|| (result.group && result.group.data && (result.group.data.id || result.group.data.registryId)))) || null;
|
|
2555
|
+
const letterIds = (result && Array.isArray(result.letters))
|
|
2556
|
+
? result.letters.map(function(l){ return (l && l.data && (l.data.id || l.data.registryId)) || null; }).filter(Boolean)
|
|
2557
|
+
: [];
|
|
2558
|
+
if (!collageId) {
|
|
2559
|
+
return { success: false, error: 'createLetterCollage returned no id', letterCount: letterIds.length, letterIds: letterIds };
|
|
2560
|
+
}
|
|
2561
|
+
return { success: true, collageId: collageId, itemId: collageId, letterCount: letterIds.length, letterIds: letterIds };
|
|
2358
2562
|
})();
|
|
2359
2563
|
`.trim();
|
|
2360
2564
|
}
|
|
@@ -3465,6 +3669,1219 @@ ${mask ? ` app.imageTools.applyMask(raster, '${mask}');\n` : ''} const ite
|
|
|
3465
3669
|
})();
|
|
3466
3670
|
`.trim();
|
|
3467
3671
|
}
|
|
3672
|
+
generateManageScenes(input) {
|
|
3673
|
+
const guard = `if (!app.sceneManager) return { error: 'SceneManager not available' };`;
|
|
3674
|
+
switch (input.action) {
|
|
3675
|
+
case 'save':
|
|
3676
|
+
return `
|
|
3677
|
+
// Save current canvas as scene
|
|
3678
|
+
(async function() {
|
|
3679
|
+
${guard}
|
|
3680
|
+
try {
|
|
3681
|
+
const result = await app.sceneManager.saveCurrentAsScene(${JSON.stringify(input.name || 'Untitled Scene')});
|
|
3682
|
+
return { success: true, action: 'save', sceneId: result?.id || result, name: ${JSON.stringify(input.name || 'Untitled Scene')} };
|
|
3683
|
+
} catch (e) {
|
|
3684
|
+
return { error: 'Failed to save scene: ' + e.message };
|
|
3685
|
+
}
|
|
3686
|
+
})();
|
|
3687
|
+
`.trim();
|
|
3688
|
+
case 'load':
|
|
3689
|
+
return `
|
|
3690
|
+
// Load scene
|
|
3691
|
+
(async function() {
|
|
3692
|
+
${guard}
|
|
3693
|
+
try {
|
|
3694
|
+
await app.sceneManager.loadScene(${JSON.stringify(input.sceneId)}${input.transition ? `, { transition: ${JSON.stringify(input.transition)} }` : ''});
|
|
3695
|
+
return { success: true, action: 'load', sceneId: ${JSON.stringify(input.sceneId)}${input.transition ? `, transition: ${JSON.stringify(input.transition)}` : ''} };
|
|
3696
|
+
} catch (e) {
|
|
3697
|
+
return { error: 'Failed to load scene: ' + e.message };
|
|
3698
|
+
}
|
|
3699
|
+
})();
|
|
3700
|
+
`.trim();
|
|
3701
|
+
case 'list':
|
|
3702
|
+
return `
|
|
3703
|
+
// List all scenes
|
|
3704
|
+
(function() {
|
|
3705
|
+
${guard}
|
|
3706
|
+
const scenes = app.sceneManager.listScenes();
|
|
3707
|
+
return { success: true, action: 'list', scenes: scenes, count: scenes.length };
|
|
3708
|
+
})();
|
|
3709
|
+
`.trim();
|
|
3710
|
+
case 'delete':
|
|
3711
|
+
return `
|
|
3712
|
+
// Delete scene
|
|
3713
|
+
(function() {
|
|
3714
|
+
${guard}
|
|
3715
|
+
app.sceneManager.deleteScene(${JSON.stringify(input.sceneId)});
|
|
3716
|
+
return { success: true, action: 'delete', sceneId: ${JSON.stringify(input.sceneId)} };
|
|
3717
|
+
})();
|
|
3718
|
+
`.trim();
|
|
3719
|
+
case 'rename':
|
|
3720
|
+
return `
|
|
3721
|
+
// Rename scene
|
|
3722
|
+
(function() {
|
|
3723
|
+
${guard}
|
|
3724
|
+
app.sceneManager.renameScene(${JSON.stringify(input.sceneId)}, ${JSON.stringify(input.name)});
|
|
3725
|
+
return { success: true, action: 'rename', sceneId: ${JSON.stringify(input.sceneId)}, name: ${JSON.stringify(input.name)} };
|
|
3726
|
+
})();
|
|
3727
|
+
`.trim();
|
|
3728
|
+
case 'duplicate':
|
|
3729
|
+
return `
|
|
3730
|
+
// Duplicate scene
|
|
3731
|
+
(function() {
|
|
3732
|
+
${guard}
|
|
3733
|
+
const result = app.sceneManager.duplicateScene(${JSON.stringify(input.sceneId)});
|
|
3734
|
+
return { success: true, action: 'duplicate', originalId: ${JSON.stringify(input.sceneId)}, newScene: result };
|
|
3735
|
+
})();
|
|
3736
|
+
`.trim();
|
|
3737
|
+
case 'reorder':
|
|
3738
|
+
return `
|
|
3739
|
+
// Reorder scenes
|
|
3740
|
+
(function() {
|
|
3741
|
+
${guard}
|
|
3742
|
+
app.sceneManager.reorderScenes(${JSON.stringify(input.sceneIds)});
|
|
3743
|
+
return { success: true, action: 'reorder', order: ${JSON.stringify(input.sceneIds)} };
|
|
3744
|
+
})();
|
|
3745
|
+
`.trim();
|
|
3746
|
+
case 'info':
|
|
3747
|
+
return `
|
|
3748
|
+
// Get scene info
|
|
3749
|
+
(function() {
|
|
3750
|
+
${guard}
|
|
3751
|
+
const scene = app.sceneManager.getScene(${JSON.stringify(input.sceneId)});
|
|
3752
|
+
if (!scene) return { error: 'Scene not found: ${input.sceneId}' };
|
|
3753
|
+
return { success: true, action: 'info', scene: scene };
|
|
3754
|
+
})();
|
|
3755
|
+
`.trim();
|
|
3756
|
+
case 'export':
|
|
3757
|
+
return `
|
|
3758
|
+
// Export all scenes
|
|
3759
|
+
(function() {
|
|
3760
|
+
${guard}
|
|
3761
|
+
const data = app.sceneManager.exportScenes();
|
|
3762
|
+
return { success: true, action: 'export', data: data };
|
|
3763
|
+
})();
|
|
3764
|
+
`.trim();
|
|
3765
|
+
case 'import':
|
|
3766
|
+
return `
|
|
3767
|
+
// Import scenes
|
|
3768
|
+
(function() {
|
|
3769
|
+
${guard}
|
|
3770
|
+
try {
|
|
3771
|
+
const json = ${JSON.stringify(input.scenesJson)};
|
|
3772
|
+
app.sceneManager.importScenes(json${input.merge !== undefined ? `, { merge: ${input.merge} }` : ''});
|
|
3773
|
+
return { success: true, action: 'import'${input.merge !== undefined ? `, merge: ${input.merge}` : ''} };
|
|
3774
|
+
} catch (e) {
|
|
3775
|
+
return { error: 'Failed to import scenes: ' + e.message };
|
|
3776
|
+
}
|
|
3777
|
+
})();
|
|
3778
|
+
`.trim();
|
|
3779
|
+
default:
|
|
3780
|
+
return `(function() { return { error: 'Unknown manage_scenes action: ${input.action}' }; })();`;
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
generateScenePlayback(input) {
|
|
3784
|
+
const guard = `if (!app.sceneManager) return { error: 'SceneManager not available' };`;
|
|
3785
|
+
switch (input.action) {
|
|
3786
|
+
case 'create_chain': {
|
|
3787
|
+
const opts = [];
|
|
3788
|
+
if (input.loop !== undefined)
|
|
3789
|
+
opts.push(`loop: ${input.loop}`);
|
|
3790
|
+
if (input.autoPlay !== undefined)
|
|
3791
|
+
opts.push(`autoPlay: ${input.autoPlay}`);
|
|
3792
|
+
if (input.defaultDuration !== undefined)
|
|
3793
|
+
opts.push(`defaultDuration: ${input.defaultDuration * 1000}`);
|
|
3794
|
+
if (input.defaultTransition !== undefined)
|
|
3795
|
+
opts.push(`defaultTransition: ${JSON.stringify(input.defaultTransition)}`);
|
|
3796
|
+
if (input.transitionDuration !== undefined)
|
|
3797
|
+
opts.push(`transitionDuration: ${input.transitionDuration * 1000}`);
|
|
3798
|
+
const optsStr = opts.length > 0 ? `, { ${opts.join(', ')} }` : '';
|
|
3799
|
+
return `
|
|
3800
|
+
// Create scene chain for sequential playback
|
|
3801
|
+
(function() {
|
|
3802
|
+
${guard}
|
|
3803
|
+
app.sceneManager.createChain(${JSON.stringify(input.sceneIds)}${optsStr});
|
|
3804
|
+
return { success: true, action: 'create_chain', sceneIds: ${JSON.stringify(input.sceneIds)}${input.loop !== undefined ? `, loop: ${input.loop}` : ''}${input.defaultDuration !== undefined ? `, defaultDuration: ${input.defaultDuration}` : ''} };
|
|
3805
|
+
})();
|
|
3806
|
+
`.trim();
|
|
3807
|
+
}
|
|
3808
|
+
case 'play':
|
|
3809
|
+
return `
|
|
3810
|
+
// Play scene chain
|
|
3811
|
+
(async function() {
|
|
3812
|
+
${guard}
|
|
3813
|
+
try {
|
|
3814
|
+
await app.sceneManager.playChain();
|
|
3815
|
+
return { success: true, action: 'play' };
|
|
3816
|
+
} catch (e) {
|
|
3817
|
+
return { error: 'Failed to play chain: ' + e.message };
|
|
3818
|
+
}
|
|
3819
|
+
})();
|
|
3820
|
+
`.trim();
|
|
3821
|
+
case 'pause':
|
|
3822
|
+
return `
|
|
3823
|
+
// Pause scene chain
|
|
3824
|
+
(function() {
|
|
3825
|
+
${guard}
|
|
3826
|
+
app.sceneManager.pauseChain();
|
|
3827
|
+
return { success: true, action: 'pause' };
|
|
3828
|
+
})();
|
|
3829
|
+
`.trim();
|
|
3830
|
+
case 'resume':
|
|
3831
|
+
return `
|
|
3832
|
+
// Resume scene chain
|
|
3833
|
+
(function() {
|
|
3834
|
+
${guard}
|
|
3835
|
+
app.sceneManager.resumeChain();
|
|
3836
|
+
return { success: true, action: 'resume' };
|
|
3837
|
+
})();
|
|
3838
|
+
`.trim();
|
|
3839
|
+
case 'stop':
|
|
3840
|
+
return `
|
|
3841
|
+
// Stop scene chain
|
|
3842
|
+
(function() {
|
|
3843
|
+
${guard}
|
|
3844
|
+
app.sceneManager.stopChain();
|
|
3845
|
+
return { success: true, action: 'stop' };
|
|
3846
|
+
})();
|
|
3847
|
+
`.trim();
|
|
3848
|
+
case 'toggle_loop':
|
|
3849
|
+
return `
|
|
3850
|
+
// Toggle loop mode
|
|
3851
|
+
(function() {
|
|
3852
|
+
${guard}
|
|
3853
|
+
app.sceneManager.setLoop(${input.enabled ?? true});
|
|
3854
|
+
return { success: true, action: 'toggle_loop', enabled: ${input.enabled ?? true} };
|
|
3855
|
+
})();
|
|
3856
|
+
`.trim();
|
|
3857
|
+
case 'jump':
|
|
3858
|
+
return `
|
|
3859
|
+
// Jump to scene index
|
|
3860
|
+
(async function() {
|
|
3861
|
+
${guard}
|
|
3862
|
+
try {
|
|
3863
|
+
await app.sceneManager.jumpToChainIndex(${input.index ?? 0});
|
|
3864
|
+
return { success: true, action: 'jump', index: ${input.index ?? 0} };
|
|
3865
|
+
} catch (e) {
|
|
3866
|
+
return { error: 'Failed to jump to index: ' + e.message };
|
|
3867
|
+
}
|
|
3868
|
+
})();
|
|
3869
|
+
`.trim();
|
|
3870
|
+
default:
|
|
3871
|
+
return `(function() { return { error: 'Unknown scene_playback action: ${input.action}' }; })();`;
|
|
3872
|
+
}
|
|
3873
|
+
}
|
|
3874
|
+
// ===========================================================================
|
|
3875
|
+
// SELECTION, TRANSFORM & HISTORY
|
|
3876
|
+
// ===========================================================================
|
|
3877
|
+
generateSelection(input) {
|
|
3878
|
+
switch (input.action) {
|
|
3879
|
+
case 'select': {
|
|
3880
|
+
const ids = JSON.stringify(input.itemIds || []);
|
|
3881
|
+
const mode = input.mode || 'replace';
|
|
3882
|
+
return `
|
|
3883
|
+
// Select items
|
|
3884
|
+
(function() {
|
|
3885
|
+
const ids = ${ids};
|
|
3886
|
+
if (ids.length === 0) return { error: 'No itemIds provided' };
|
|
3887
|
+
${mode === 'replace' ? 'app.deselectAll();' : ''}
|
|
3888
|
+
const selected = [];
|
|
3889
|
+
for (const id of ids) {
|
|
3890
|
+
const entry = app.itemRegistry.get(id);
|
|
3891
|
+
if (entry && entry.item) {
|
|
3892
|
+
${mode === 'remove' ? 'entry.item.selected = false;' : 'app.select(entry.item);'}
|
|
3893
|
+
selected.push(id);
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
return { success: true, action: 'select', mode: '${mode}', selected };
|
|
3897
|
+
})();`.trim();
|
|
3898
|
+
}
|
|
3899
|
+
case 'select_all':
|
|
3900
|
+
return `
|
|
3901
|
+
// Select all items
|
|
3902
|
+
(function() {
|
|
3903
|
+
app.selectAll();
|
|
3904
|
+
const items = app.getSelection().map(i => i.data?.itemId || i.name || i.id);
|
|
3905
|
+
return { success: true, action: 'select_all', count: items.length, items };
|
|
3906
|
+
})();`.trim();
|
|
3907
|
+
case 'deselect_all':
|
|
3908
|
+
return `
|
|
3909
|
+
// Deselect all items
|
|
3910
|
+
(function() {
|
|
3911
|
+
app.deselectAll();
|
|
3912
|
+
return { success: true, action: 'deselect_all' };
|
|
3913
|
+
})();`.trim();
|
|
3914
|
+
case 'get':
|
|
3915
|
+
return `
|
|
3916
|
+
// Get current selection
|
|
3917
|
+
(function() {
|
|
3918
|
+
const items = app.getSelection().map(i => ({
|
|
3919
|
+
itemId: i.data?.itemId || i.name || i.id,
|
|
3920
|
+
type: i.data?.itemType || i.className,
|
|
3921
|
+
bounds: i.bounds ? { x: i.bounds.x, y: i.bounds.y, width: i.bounds.width, height: i.bounds.height } : null
|
|
3922
|
+
}));
|
|
3923
|
+
return { success: true, action: 'get', count: items.length, items };
|
|
3924
|
+
})();`.trim();
|
|
3925
|
+
case 'delete_selected':
|
|
3926
|
+
return `
|
|
3927
|
+
// Delete selected items
|
|
3928
|
+
(function() {
|
|
3929
|
+
const selection = app.getSelection();
|
|
3930
|
+
const deleted = selection.map(i => i.data?.itemId || i.name || i.id);
|
|
3931
|
+
app.deleteSelected();
|
|
3932
|
+
return { success: true, action: 'delete_selected', deleted, count: deleted.length };
|
|
3933
|
+
})();`.trim();
|
|
3934
|
+
default:
|
|
3935
|
+
return `(function() { return { error: 'Unknown selection action: ${input.action}' }; })();`;
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
generateTransform(input) {
|
|
3939
|
+
switch (input.action) {
|
|
3940
|
+
case 'nudge': {
|
|
3941
|
+
const dx = input.dx ?? 0;
|
|
3942
|
+
const dy = input.dy ?? 0;
|
|
3943
|
+
return `
|
|
3944
|
+
// Nudge item position
|
|
3945
|
+
(function() {
|
|
3946
|
+
const entry = app.itemRegistry.get(${JSON.stringify(input.itemId || '')});
|
|
3947
|
+
if (!entry || !entry.item) return { error: 'Item not found: ${input.itemId}' };
|
|
3948
|
+
entry.item.position.x += ${dx};
|
|
3949
|
+
entry.item.position.y += ${dy};
|
|
3950
|
+
return { success: true, action: 'nudge', itemId: '${input.itemId}', dx: ${dx}, dy: ${dy}, position: { x: entry.item.position.x, y: entry.item.position.y } };
|
|
3951
|
+
})();`.trim();
|
|
3952
|
+
}
|
|
3953
|
+
case 'flip': {
|
|
3954
|
+
const dir = input.direction || 'horizontal';
|
|
3955
|
+
return `
|
|
3956
|
+
// Flip item ${dir}ly
|
|
3957
|
+
(function() {
|
|
3958
|
+
const entry = app.itemRegistry.get(${JSON.stringify(input.itemId || '')});
|
|
3959
|
+
if (!entry || !entry.item) return { error: 'Item not found: ${input.itemId}' };
|
|
3960
|
+
${dir === 'horizontal' ? 'entry.item.scale(-1, 1);' : 'entry.item.scale(1, -1);'}
|
|
3961
|
+
return { success: true, action: 'flip', itemId: '${input.itemId}', direction: '${dir}' };
|
|
3962
|
+
})();`.trim();
|
|
3963
|
+
}
|
|
3964
|
+
case 'reorder': {
|
|
3965
|
+
const order = input.order || 'bringToFront';
|
|
3966
|
+
const methodMap = {
|
|
3967
|
+
bringToFront: 'bringToFront',
|
|
3968
|
+
sendToBack: 'sendToBack',
|
|
3969
|
+
moveUp: 'moveAbove',
|
|
3970
|
+
moveDown: 'moveBelow',
|
|
3971
|
+
};
|
|
3972
|
+
const method = methodMap[order] || 'bringToFront';
|
|
3973
|
+
const needsSibling = order === 'moveUp' || order === 'moveDown';
|
|
3974
|
+
if (needsSibling) {
|
|
3975
|
+
const siblingExpr = order === 'moveUp' ? 'entry.item.nextSibling' : 'entry.item.previousSibling';
|
|
3976
|
+
return `
|
|
3977
|
+
// Reorder item: ${order}
|
|
3978
|
+
(function() {
|
|
3979
|
+
const entry = app.itemRegistry.get(${JSON.stringify(input.itemId || '')});
|
|
3980
|
+
if (!entry || !entry.item) return { error: 'Item not found: ${input.itemId}' };
|
|
3981
|
+
const sibling = ${siblingExpr};
|
|
3982
|
+
if (sibling) entry.item.${method}(sibling);
|
|
3983
|
+
return { success: true, action: 'reorder', itemId: '${input.itemId}', order: '${order}' };
|
|
3984
|
+
})();`.trim();
|
|
3985
|
+
}
|
|
3986
|
+
return `
|
|
3987
|
+
// Reorder item: ${order}
|
|
3988
|
+
(function() {
|
|
3989
|
+
const entry = app.itemRegistry.get(${JSON.stringify(input.itemId || '')});
|
|
3990
|
+
if (!entry || !entry.item) return { error: 'Item not found: ${input.itemId}' };
|
|
3991
|
+
entry.item.${method}();
|
|
3992
|
+
return { success: true, action: 'reorder', itemId: '${input.itemId}', order: '${order}' };
|
|
3993
|
+
})();`.trim();
|
|
3994
|
+
}
|
|
3995
|
+
default:
|
|
3996
|
+
return `(function() { return { error: 'Unknown transform action: ${input.action}' }; })();`;
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
generateHistory(input) {
|
|
4000
|
+
const guard = `if (!app.historyManager) return { error: 'HistoryManager not available' };`;
|
|
4001
|
+
switch (input.action) {
|
|
4002
|
+
case 'undo':
|
|
4003
|
+
return `
|
|
4004
|
+
// Undo last action
|
|
4005
|
+
(function() {
|
|
4006
|
+
${guard}
|
|
4007
|
+
app.historyManager.undo();
|
|
4008
|
+
return { success: true, action: 'undo' };
|
|
4009
|
+
})();`.trim();
|
|
4010
|
+
case 'redo':
|
|
4011
|
+
return `
|
|
4012
|
+
// Redo last undone action
|
|
4013
|
+
(function() {
|
|
4014
|
+
${guard}
|
|
4015
|
+
app.historyManager.redo();
|
|
4016
|
+
return { success: true, action: 'redo' };
|
|
4017
|
+
})();`.trim();
|
|
4018
|
+
case 'get_state':
|
|
4019
|
+
return `
|
|
4020
|
+
// Get history state
|
|
4021
|
+
(function() {
|
|
4022
|
+
${guard}
|
|
4023
|
+
const state = app.historyManager.getState();
|
|
4024
|
+
return { success: true, action: 'get_state', ...state };
|
|
4025
|
+
})();`.trim();
|
|
4026
|
+
default:
|
|
4027
|
+
return `(function() { return { error: 'Unknown history action: ${input.action}' }; })();`;
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
// ===========================================================================
|
|
4031
|
+
// IMAGE PROCESSING
|
|
4032
|
+
// ===========================================================================
|
|
4033
|
+
generateImageFilter(input) {
|
|
4034
|
+
const guard = `if (!app.imageTools) return { error: 'ImageTools not available' };`;
|
|
4035
|
+
switch (input.action) {
|
|
4036
|
+
case 'apply':
|
|
4037
|
+
return `
|
|
4038
|
+
// Apply image filter
|
|
4039
|
+
(async function() {
|
|
4040
|
+
${guard}
|
|
4041
|
+
try {
|
|
4042
|
+
const result = await app.imageTools.applyFilter(${JSON.stringify(input.itemId)}, ${JSON.stringify(input.filterName || '')}, ${JSON.stringify(input.params || {})});
|
|
4043
|
+
return { success: true, action: 'apply', itemId: ${JSON.stringify(input.itemId)}, filter: ${JSON.stringify(input.filterName || '')} };
|
|
4044
|
+
} catch (e) {
|
|
4045
|
+
return { error: 'Failed to apply filter: ' + e.message };
|
|
4046
|
+
}
|
|
4047
|
+
})();`.trim();
|
|
4048
|
+
case 'chain':
|
|
4049
|
+
return `
|
|
4050
|
+
// Apply filter chain
|
|
4051
|
+
(async function() {
|
|
4052
|
+
${guard}
|
|
4053
|
+
try {
|
|
4054
|
+
const filters = ${JSON.stringify(input.filters || [])};
|
|
4055
|
+
const result = await app.imageTools.applyFilterChain(${JSON.stringify(input.itemId)}, filters);
|
|
4056
|
+
return { success: true, action: 'chain', itemId: ${JSON.stringify(input.itemId)}, filterCount: filters.length };
|
|
4057
|
+
} catch (e) {
|
|
4058
|
+
return { error: 'Failed to apply filter chain: ' + e.message };
|
|
4059
|
+
}
|
|
4060
|
+
})();`.trim();
|
|
4061
|
+
default:
|
|
4062
|
+
return `(function() { return { error: 'Unknown image_filter action: ${input.action}' }; })();`;
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
generateLasso(input) {
|
|
4066
|
+
const guard = `if (!app.imageTools) return { error: 'ImageTools not available' };`;
|
|
4067
|
+
switch (input.action) {
|
|
4068
|
+
case 'activate':
|
|
4069
|
+
return `
|
|
4070
|
+
// Activate lasso selection
|
|
4071
|
+
(async function() {
|
|
4072
|
+
${guard}
|
|
4073
|
+
try {
|
|
4074
|
+
await app.imageTools.activateLasso(${JSON.stringify(input.itemId || '')});
|
|
4075
|
+
return { success: true, action: 'activate', itemId: ${JSON.stringify(input.itemId || '')} };
|
|
4076
|
+
} catch (e) {
|
|
4077
|
+
return { error: 'Failed to activate lasso: ' + e.message };
|
|
4078
|
+
}
|
|
4079
|
+
})();`.trim();
|
|
4080
|
+
case 'apply':
|
|
4081
|
+
return `
|
|
4082
|
+
// Apply lasso selection
|
|
4083
|
+
(async function() {
|
|
4084
|
+
${guard}
|
|
4085
|
+
try {
|
|
4086
|
+
const result = await app.imageTools.applyLasso();
|
|
4087
|
+
return { success: true, action: 'apply' };
|
|
4088
|
+
} catch (e) {
|
|
4089
|
+
return { error: 'Failed to apply lasso: ' + e.message };
|
|
4090
|
+
}
|
|
4091
|
+
})();`.trim();
|
|
4092
|
+
default:
|
|
4093
|
+
return `(function() { return { error: 'Unknown lasso action: ${input.action}' }; })();`;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
generateCutoutStyle(input) {
|
|
4097
|
+
const guard = `if (!app.imageTools) return { error: 'ImageTools not available' };`;
|
|
4098
|
+
switch (input.action) {
|
|
4099
|
+
case 'apply':
|
|
4100
|
+
return `
|
|
4101
|
+
// Apply cutout style
|
|
4102
|
+
(async function() {
|
|
4103
|
+
${guard}
|
|
4104
|
+
try {
|
|
4105
|
+
const result = await app.imageTools.applyCutoutStyle(${JSON.stringify(input.itemId || '')}, ${JSON.stringify(input.preset || '')}, ${JSON.stringify(input.options || {})});
|
|
4106
|
+
return { success: true, action: 'apply', itemId: ${JSON.stringify(input.itemId || '')}, preset: ${JSON.stringify(input.preset || '')} };
|
|
4107
|
+
} catch (e) {
|
|
4108
|
+
return { error: 'Failed to apply cutout style: ' + e.message };
|
|
4109
|
+
}
|
|
4110
|
+
})();`.trim();
|
|
4111
|
+
case 'list':
|
|
4112
|
+
return `
|
|
4113
|
+
// List available cutout styles
|
|
4114
|
+
(function() {
|
|
4115
|
+
${guard}
|
|
4116
|
+
const styles = app.imageTools.getCutoutStyles();
|
|
4117
|
+
return { success: true, action: 'list', styles };
|
|
4118
|
+
})();`.trim();
|
|
4119
|
+
default:
|
|
4120
|
+
return `(function() { return { error: 'Unknown cutout_style action: ${input.action}' }; })();`;
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
// ===========================================================================
|
|
4124
|
+
// COMPOSITION & VIEW
|
|
4125
|
+
// ===========================================================================
|
|
4126
|
+
generatePrecomp(input) {
|
|
4127
|
+
switch (input.action) {
|
|
4128
|
+
case 'create': {
|
|
4129
|
+
const opts = {};
|
|
4130
|
+
if (input.name)
|
|
4131
|
+
opts.name = input.name;
|
|
4132
|
+
if (input.loop !== undefined)
|
|
4133
|
+
opts.loop = input.loop;
|
|
4134
|
+
if (input.duration !== undefined)
|
|
4135
|
+
opts.duration = input.duration;
|
|
4136
|
+
return `
|
|
4137
|
+
// Create precomp from items
|
|
4138
|
+
(function() {
|
|
4139
|
+
const itemIds = ${JSON.stringify(input.itemIds || [])};
|
|
4140
|
+
const opts = ${JSON.stringify(opts)};
|
|
4141
|
+
const precomp = app.createPrecomp(itemIds, opts);
|
|
4142
|
+
return { success: true, action: 'create', precompId: precomp?.id || precomp, name: ${JSON.stringify(input.name || '')} };
|
|
4143
|
+
})();`.trim();
|
|
4144
|
+
}
|
|
4145
|
+
case 'add':
|
|
4146
|
+
return `
|
|
4147
|
+
// Add item to precomp
|
|
4148
|
+
(function() {
|
|
4149
|
+
app.addToPrecomp(${JSON.stringify(input.precompId || '')}, ${JSON.stringify(input.itemId || '')});
|
|
4150
|
+
return { success: true, action: 'add', precompId: ${JSON.stringify(input.precompId || '')}, itemId: ${JSON.stringify(input.itemId || '')} };
|
|
4151
|
+
})();`.trim();
|
|
4152
|
+
case 'remove':
|
|
4153
|
+
return `
|
|
4154
|
+
// Remove item from precomp
|
|
4155
|
+
(function() {
|
|
4156
|
+
app.removeFromPrecomp(${JSON.stringify(input.precompId || '')}, ${JSON.stringify(input.itemId || '')});
|
|
4157
|
+
return { success: true, action: 'remove', precompId: ${JSON.stringify(input.precompId || '')}, itemId: ${JSON.stringify(input.itemId || '')} };
|
|
4158
|
+
})();`.trim();
|
|
4159
|
+
default:
|
|
4160
|
+
return `(function() { return { error: 'Unknown precomp action: ${input.action}' }; })();`;
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
generateView(input) {
|
|
4164
|
+
switch (input.action) {
|
|
4165
|
+
case 'fit': {
|
|
4166
|
+
const mode = input.mode || 'content';
|
|
4167
|
+
const padding = input.padding ?? 20;
|
|
4168
|
+
return `
|
|
4169
|
+
// Fit view to ${mode}
|
|
4170
|
+
(function() {
|
|
4171
|
+
app.fitView('${mode}', ${padding});
|
|
4172
|
+
return { success: true, action: 'fit', mode: '${mode}', padding: ${padding} };
|
|
4173
|
+
})();`.trim();
|
|
4174
|
+
}
|
|
4175
|
+
case 'get_state':
|
|
4176
|
+
return `
|
|
4177
|
+
// Get current view state
|
|
4178
|
+
(function() {
|
|
4179
|
+
const state = app.getViewState();
|
|
4180
|
+
return { success: true, action: 'get_state', ...state };
|
|
4181
|
+
})();`.trim();
|
|
4182
|
+
default:
|
|
4183
|
+
return `(function() { return { error: 'Unknown view action: ${input.action}' }; })();`;
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
generateBackground(input) {
|
|
4187
|
+
switch (input.action) {
|
|
4188
|
+
case 'set': {
|
|
4189
|
+
const mode = input.mode || 'color';
|
|
4190
|
+
if (mode === 'generator') {
|
|
4191
|
+
return `
|
|
4192
|
+
// Set background via generator
|
|
4193
|
+
(async function() {
|
|
4194
|
+
try {
|
|
4195
|
+
await app.executeGenerator(${JSON.stringify(input.generator || '')}, ${JSON.stringify(input.generatorParams || {})});
|
|
4196
|
+
return { success: true, action: 'set', mode: 'generator', generator: ${JSON.stringify(input.generator || '')} };
|
|
4197
|
+
} catch (e) {
|
|
4198
|
+
return { error: 'Failed to set generator background: ' + e.message };
|
|
4199
|
+
}
|
|
4200
|
+
})();`.trim();
|
|
4201
|
+
}
|
|
4202
|
+
if (mode === 'pattern') {
|
|
4203
|
+
return `
|
|
4204
|
+
// Set background pattern
|
|
4205
|
+
(function() {
|
|
4206
|
+
app.setBackgroundPattern(${JSON.stringify(input.pattern || '')});
|
|
4207
|
+
return { success: true, action: 'set', mode: 'pattern', pattern: ${JSON.stringify(input.pattern || '')} };
|
|
4208
|
+
})();`.trim();
|
|
4209
|
+
}
|
|
4210
|
+
// default: color
|
|
4211
|
+
return `
|
|
4212
|
+
// Set background color
|
|
4213
|
+
(function() {
|
|
4214
|
+
app.setBackgroundColor(${JSON.stringify(input.color || '#ffffff')});
|
|
4215
|
+
return { success: true, action: 'set', mode: 'color', color: ${JSON.stringify(input.color || '#ffffff')} };
|
|
4216
|
+
})();`.trim();
|
|
4217
|
+
}
|
|
4218
|
+
case 'clear':
|
|
4219
|
+
return `
|
|
4220
|
+
// Clear background
|
|
4221
|
+
(function() {
|
|
4222
|
+
app.clearBackground();
|
|
4223
|
+
return { success: true, action: 'clear' };
|
|
4224
|
+
})();`.trim();
|
|
4225
|
+
case 'get':
|
|
4226
|
+
return `
|
|
4227
|
+
// Get background info
|
|
4228
|
+
(function() {
|
|
4229
|
+
const bg = app.getBackground();
|
|
4230
|
+
return { success: true, action: 'get', ...bg };
|
|
4231
|
+
})();`.trim();
|
|
4232
|
+
default:
|
|
4233
|
+
return `(function() { return { error: 'Unknown background action: ${input.action}' }; })();`;
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
// ===========================================================================
|
|
4237
|
+
// CANVAS QUERIES
|
|
4238
|
+
// ===========================================================================
|
|
4239
|
+
generateQuery(input) {
|
|
4240
|
+
switch (input.action) {
|
|
4241
|
+
case 'get_by_id':
|
|
4242
|
+
return `
|
|
4243
|
+
// Get item details by ID
|
|
4244
|
+
(function() {
|
|
4245
|
+
const entry = app.itemRegistry.get(${JSON.stringify(input.itemId || '')});
|
|
4246
|
+
if (!entry || !entry.item) return { error: 'Item not found: ${input.itemId}' };
|
|
4247
|
+
const item = entry.item;
|
|
4248
|
+
return {
|
|
4249
|
+
success: true, action: 'get_by_id',
|
|
4250
|
+
itemId: ${JSON.stringify(input.itemId || '')},
|
|
4251
|
+
type: item.data?.itemType || item.className,
|
|
4252
|
+
position: { x: item.position.x, y: item.position.y },
|
|
4253
|
+
bounds: item.bounds ? { x: item.bounds.x, y: item.bounds.y, width: item.bounds.width, height: item.bounds.height } : null,
|
|
4254
|
+
visible: item.visible,
|
|
4255
|
+
opacity: item.opacity,
|
|
4256
|
+
rotation: item.rotation,
|
|
4257
|
+
selected: item.selected,
|
|
4258
|
+
};
|
|
4259
|
+
})();`.trim();
|
|
4260
|
+
case 'hit_test': {
|
|
4261
|
+
const tolerance = input.tolerance ?? 5;
|
|
4262
|
+
const all = input.all ?? false;
|
|
4263
|
+
return `
|
|
4264
|
+
// Hit test at point
|
|
4265
|
+
(function() {
|
|
4266
|
+
const point = new paper.Point(${input.x ?? 0}, ${input.y ?? 0});
|
|
4267
|
+
${all
|
|
4268
|
+
? `const results = app.hitTestAll(point, { tolerance: ${tolerance} });
|
|
4269
|
+
const hits = results.map(r => ({
|
|
4270
|
+
itemId: r.item?.data?.itemId || r.item?.name || r.item?.id,
|
|
4271
|
+
type: r.item?.data?.itemType || r.item?.className,
|
|
4272
|
+
point: { x: r.point?.x, y: r.point?.y }
|
|
4273
|
+
}));
|
|
4274
|
+
return { success: true, action: 'hit_test', x: ${input.x ?? 0}, y: ${input.y ?? 0}, hits, count: hits.length };`
|
|
4275
|
+
: `const result = app.hitTest(point, { tolerance: ${tolerance} });
|
|
4276
|
+
if (!result) return { success: true, action: 'hit_test', x: ${input.x ?? 0}, y: ${input.y ?? 0}, hit: null };
|
|
4277
|
+
return {
|
|
4278
|
+
success: true, action: 'hit_test',
|
|
4279
|
+
x: ${input.x ?? 0}, y: ${input.y ?? 0},
|
|
4280
|
+
hit: { itemId: result.item?.data?.itemId || result.item?.name || result.item?.id, type: result.item?.data?.itemType || result.item?.className }
|
|
4281
|
+
};`}
|
|
4282
|
+
})();`.trim();
|
|
4283
|
+
}
|
|
4284
|
+
case 'is_empty':
|
|
4285
|
+
return `
|
|
4286
|
+
// Check if canvas is empty
|
|
4287
|
+
(function() {
|
|
4288
|
+
const items = app.itemRegistry.getAll();
|
|
4289
|
+
return { success: true, action: 'is_empty', empty: items.length === 0, count: items.length };
|
|
4290
|
+
})();`.trim();
|
|
4291
|
+
default:
|
|
4292
|
+
return `(function() { return { error: 'Unknown query action: ${input.action}' }; })();`;
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
// ===========================================================================
|
|
4296
|
+
// DEFORMATION
|
|
4297
|
+
// ===========================================================================
|
|
4298
|
+
generateDeform(input) {
|
|
4299
|
+
const guard = `if (!app.deformPresets) return { error: 'DeformPresets not available' };`;
|
|
4300
|
+
switch (input.action) {
|
|
4301
|
+
case 'apply': {
|
|
4302
|
+
if (!input.itemId)
|
|
4303
|
+
return `(function() { return { error: 'itemId is required for deform apply' }; })();`;
|
|
4304
|
+
if (!input.preset)
|
|
4305
|
+
return `(function() { return { error: 'preset is required for deform apply' }; })();`;
|
|
4306
|
+
const params = {};
|
|
4307
|
+
if (input.frequency !== undefined)
|
|
4308
|
+
params.frequency = input.frequency;
|
|
4309
|
+
if (input.amplitude !== undefined)
|
|
4310
|
+
params.amplitude = input.amplitude;
|
|
4311
|
+
if (input.phase)
|
|
4312
|
+
params.phase = input.phase;
|
|
4313
|
+
if (input.loop !== undefined)
|
|
4314
|
+
params.loop = input.loop;
|
|
4315
|
+
if (input.axis)
|
|
4316
|
+
params.axis = input.axis;
|
|
4317
|
+
if (input.turns !== undefined)
|
|
4318
|
+
params.turns = input.turns;
|
|
4319
|
+
if (input.waves !== undefined)
|
|
4320
|
+
params.waves = input.waves;
|
|
4321
|
+
if (input.maxDisplacement !== undefined)
|
|
4322
|
+
params.maxDisplacement = input.maxDisplacement;
|
|
4323
|
+
if (input.speed !== undefined)
|
|
4324
|
+
params.speed = input.speed;
|
|
4325
|
+
if (input.steps !== undefined)
|
|
4326
|
+
params.steps = input.steps;
|
|
4327
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4328
|
+
return `
|
|
4329
|
+
// Apply deformation preset
|
|
4330
|
+
(function() {
|
|
4331
|
+
${guard}
|
|
4332
|
+
const entry = app.itemRegistry.get(${itemIdStr});
|
|
4333
|
+
if (!entry || !entry.item) return { error: 'Item not found: ' + ${itemIdStr} };
|
|
4334
|
+
app.deformPresets.apply(entry.item, '${input.preset}', ${JSON.stringify(params)});
|
|
4335
|
+
return { success: true, action: 'apply', itemId: ${itemIdStr}, preset: '${input.preset}' };
|
|
4336
|
+
})();`.trim();
|
|
4337
|
+
}
|
|
4338
|
+
case 'trigger': {
|
|
4339
|
+
if (!input.itemId)
|
|
4340
|
+
return `(function() { return { error: 'itemId is required for deform trigger' }; })();`;
|
|
4341
|
+
if (!input.preset)
|
|
4342
|
+
return `(function() { return { error: 'preset is required for deform trigger' }; })();`;
|
|
4343
|
+
const params = {};
|
|
4344
|
+
if (input.amplitude !== undefined)
|
|
4345
|
+
params.amplitude = input.amplitude;
|
|
4346
|
+
if (input.speed !== undefined)
|
|
4347
|
+
params.speed = input.speed;
|
|
4348
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4349
|
+
return `
|
|
4350
|
+
// Trigger one-shot deformation
|
|
4351
|
+
(function() {
|
|
4352
|
+
${guard}
|
|
4353
|
+
const entry = app.itemRegistry.get(${itemIdStr});
|
|
4354
|
+
if (!entry || !entry.item) return { error: 'Item not found: ' + ${itemIdStr} };
|
|
4355
|
+
app.deformPresets.trigger(entry.item, '${input.preset}', ${JSON.stringify(params)});
|
|
4356
|
+
return { success: true, action: 'trigger', itemId: ${itemIdStr}, preset: '${input.preset}' };
|
|
4357
|
+
})();`.trim();
|
|
4358
|
+
}
|
|
4359
|
+
case 'remove': {
|
|
4360
|
+
if (!input.itemId)
|
|
4361
|
+
return `(function() { return { error: 'itemId is required for deform remove' }; })();`;
|
|
4362
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4363
|
+
return `
|
|
4364
|
+
// Remove deformation and restore geometry
|
|
4365
|
+
(function() {
|
|
4366
|
+
${guard}
|
|
4367
|
+
const entry = app.itemRegistry.get(${itemIdStr});
|
|
4368
|
+
if (!entry || !entry.item) return { error: 'Item not found: ' + ${itemIdStr} };
|
|
4369
|
+
app.deformPresets.remove(entry.item);
|
|
4370
|
+
return { success: true, action: 'remove', itemId: ${itemIdStr} };
|
|
4371
|
+
})();`.trim();
|
|
4372
|
+
}
|
|
4373
|
+
default:
|
|
4374
|
+
return `(function() { return { error: 'Unknown deform action: ${input.action}' }; })();`;
|
|
4375
|
+
}
|
|
4376
|
+
}
|
|
4377
|
+
// ===========================================================================
|
|
4378
|
+
// SPRITE SHEETS
|
|
4379
|
+
// ===========================================================================
|
|
4380
|
+
generateSpriteSheet(input) {
|
|
4381
|
+
const guard = `if (!app.spriteSheetSystem) return { error: 'SpriteSheetSystem not available' };`;
|
|
4382
|
+
switch (input.action) {
|
|
4383
|
+
case 'generate': {
|
|
4384
|
+
const opts = {};
|
|
4385
|
+
if (input.poses)
|
|
4386
|
+
opts.poses = input.poses;
|
|
4387
|
+
if (input.transition)
|
|
4388
|
+
opts.transition = input.transition;
|
|
4389
|
+
if (input.bakedAnimation)
|
|
4390
|
+
opts.bakedAnimation = input.bakedAnimation;
|
|
4391
|
+
if (input.animations)
|
|
4392
|
+
opts.animations = input.animations;
|
|
4393
|
+
if (input.padding !== undefined)
|
|
4394
|
+
opts.padding = input.padding;
|
|
4395
|
+
if (input.name)
|
|
4396
|
+
opts.name = input.name;
|
|
4397
|
+
return `
|
|
4398
|
+
// Generate sprite sheet from skeleton
|
|
4399
|
+
(async function() {
|
|
4400
|
+
${guard}
|
|
4401
|
+
const sheet = await app.spriteSheetSystem.generateSpriteSheet(${JSON.stringify(input.skeletonId || '')}, ${JSON.stringify(opts)});
|
|
4402
|
+
return { success: true, action: 'generate', spriteSheetId: sheet.id, name: sheet.name, width: sheet.atlasWidth, height: sheet.atlasHeight, frameCount: sheet.frames?.size ?? sheet.frames?.length ?? 0 };
|
|
4403
|
+
})();`.trim();
|
|
4404
|
+
}
|
|
4405
|
+
case 'play': {
|
|
4406
|
+
const opts = {};
|
|
4407
|
+
if (input.x !== undefined)
|
|
4408
|
+
opts.x = input.x;
|
|
4409
|
+
if (input.y !== undefined)
|
|
4410
|
+
opts.y = input.y;
|
|
4411
|
+
if (input.animation)
|
|
4412
|
+
opts.animation = input.animation;
|
|
4413
|
+
if (input.fps !== undefined)
|
|
4414
|
+
opts.fps = input.fps;
|
|
4415
|
+
if (input.scale !== undefined)
|
|
4416
|
+
opts.scale = input.scale;
|
|
4417
|
+
return `
|
|
4418
|
+
// Play sprite sheet animation
|
|
4419
|
+
(async function() {
|
|
4420
|
+
${guard}
|
|
4421
|
+
const player = await app.spriteSheetSystem.playSpriteSheet(${JSON.stringify(input.spriteSheetId || '')}, ${JSON.stringify(opts)});
|
|
4422
|
+
return { success: true, action: 'play', playerId: player.id };
|
|
4423
|
+
})();`.trim();
|
|
4424
|
+
}
|
|
4425
|
+
case 'export':
|
|
4426
|
+
return `
|
|
4427
|
+
// Export sprite sheet
|
|
4428
|
+
(async function() {
|
|
4429
|
+
${guard}
|
|
4430
|
+
const result = await app.spriteSheetSystem.exportSpriteSheet(${JSON.stringify(input.spriteSheetId || '')}, { format: '${input.format || 'png'}', download: true, includeMetadata: true });
|
|
4431
|
+
return { success: true, action: 'export', format: '${input.format || 'png'}' };
|
|
4432
|
+
})();`.trim();
|
|
4433
|
+
default:
|
|
4434
|
+
return `(function() { return { error: 'Unknown sprite sheet action: ${input.action}' }; })();`;
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
// ===========================================================================
|
|
4438
|
+
// STORAGE
|
|
4439
|
+
// ===========================================================================
|
|
4440
|
+
generateStorage(input) {
|
|
4441
|
+
const guard = `if (!app.storageManager) return { error: 'StorageManager not available' };`;
|
|
4442
|
+
switch (input.action) {
|
|
4443
|
+
case 'save': {
|
|
4444
|
+
const name = input.name ? JSON.stringify(input.name) : "'Untitled Project'";
|
|
4445
|
+
const thumbnailCode = input.thumbnail
|
|
4446
|
+
? `const thumbnail = app.exportThumbnail ? app.exportThumbnail() : null;\n if (thumbnail) project.thumbnail = thumbnail;`
|
|
4447
|
+
: '';
|
|
4448
|
+
return `
|
|
4449
|
+
// Save project
|
|
4450
|
+
(async function() {
|
|
4451
|
+
${guard}
|
|
4452
|
+
const project = { name: ${name}, data: app.serialize() };
|
|
4453
|
+
${thumbnailCode}
|
|
4454
|
+
const projectId = await app.storageManager.saveProject(project);
|
|
4455
|
+
return { success: true, action: 'save', projectId, name: ${name} };
|
|
4456
|
+
})();`.trim();
|
|
4457
|
+
}
|
|
4458
|
+
case 'load': {
|
|
4459
|
+
const projId = JSON.stringify(input.projectId || '');
|
|
4460
|
+
return `
|
|
4461
|
+
// Load project
|
|
4462
|
+
(async function() {
|
|
4463
|
+
${guard}
|
|
4464
|
+
const project = await app.storageManager.loadProject(${projId});
|
|
4465
|
+
if (!project) return { error: 'Project not found: ' + ${projId} };
|
|
4466
|
+
app.deserialize(project.data);
|
|
4467
|
+
return { success: true, action: 'load', projectId: ${projId}, name: project.name };
|
|
4468
|
+
})();`.trim();
|
|
4469
|
+
}
|
|
4470
|
+
case 'list':
|
|
4471
|
+
return `
|
|
4472
|
+
// List saved projects
|
|
4473
|
+
(async function() {
|
|
4474
|
+
${guard}
|
|
4475
|
+
const projects = await app.storageManager.listProjects();
|
|
4476
|
+
return { success: true, action: 'list', projects: projects.map(p => ({ id: p.id, name: p.name, updatedAt: p.updatedAt })), count: projects.length };
|
|
4477
|
+
})();`.trim();
|
|
4478
|
+
case 'delete': {
|
|
4479
|
+
const projId = JSON.stringify(input.projectId || '');
|
|
4480
|
+
return `
|
|
4481
|
+
// Delete project
|
|
4482
|
+
(async function() {
|
|
4483
|
+
${guard}
|
|
4484
|
+
await app.storageManager.deleteProject(${projId});
|
|
4485
|
+
return { success: true, action: 'delete', projectId: ${projId} };
|
|
4486
|
+
})();`.trim();
|
|
4487
|
+
}
|
|
4488
|
+
default:
|
|
4489
|
+
return `(function() { return { error: 'Unknown storage action: ${input.action}' }; })();`;
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
// ===========================================================================
|
|
4493
|
+
// INTERACTION
|
|
4494
|
+
// ===========================================================================
|
|
4495
|
+
generateInteraction(input) {
|
|
4496
|
+
const guard = `if (!app.interactionSystem) return { error: 'InteractionSystem not available' };`;
|
|
4497
|
+
switch (input.action) {
|
|
4498
|
+
case 'add_behavior': {
|
|
4499
|
+
const itemIdStr = JSON.stringify(input.itemId || '');
|
|
4500
|
+
const params = JSON.stringify(input.params || {});
|
|
4501
|
+
return `
|
|
4502
|
+
// Add behavior to item
|
|
4503
|
+
(function() {
|
|
4504
|
+
${guard}
|
|
4505
|
+
const entry = app.itemRegistry.get(${itemIdStr});
|
|
4506
|
+
if (!entry || !entry.item) return { error: 'Item not found: ' + ${itemIdStr} };
|
|
4507
|
+
const behaviorId = app.interactionSystem.addContinuousBehavior(entry.item, '${input.behaviorType}', ${params});
|
|
4508
|
+
return { success: true, action: 'add_behavior', itemId: ${itemIdStr}, behaviorType: '${input.behaviorType}', behaviorId };
|
|
4509
|
+
})();`.trim();
|
|
4510
|
+
}
|
|
4511
|
+
case 'remove_behavior': {
|
|
4512
|
+
const itemIdStr = JSON.stringify(input.itemId || '');
|
|
4513
|
+
const behaviorIdStr = JSON.stringify(input.behaviorId || '');
|
|
4514
|
+
return `
|
|
4515
|
+
// Remove behavior from item
|
|
4516
|
+
(function() {
|
|
4517
|
+
${guard}
|
|
4518
|
+
const entry = app.itemRegistry.get(${itemIdStr});
|
|
4519
|
+
if (!entry || !entry.item) return { error: 'Item not found: ' + ${itemIdStr} };
|
|
4520
|
+
app.interactionSystem.removeContinuousBehavior(entry.item, ${behaviorIdStr});
|
|
4521
|
+
return { success: true, action: 'remove_behavior', itemId: ${itemIdStr}, behaviorId: ${behaviorIdStr} };
|
|
4522
|
+
})();`.trim();
|
|
4523
|
+
}
|
|
4524
|
+
case 'trigger_action': {
|
|
4525
|
+
const params = JSON.stringify(input.params || {});
|
|
4526
|
+
return `
|
|
4527
|
+
// Trigger interaction action
|
|
4528
|
+
(function() {
|
|
4529
|
+
${guard}
|
|
4530
|
+
app.interactionSystem.triggerAction('${input.actionType}', ${params});
|
|
4531
|
+
return { success: true, action: 'trigger_action', actionType: '${input.actionType}' };
|
|
4532
|
+
})();`.trim();
|
|
4533
|
+
}
|
|
4534
|
+
case 'get_state':
|
|
4535
|
+
return `
|
|
4536
|
+
// Get interaction state
|
|
4537
|
+
(function() {
|
|
4538
|
+
${guard}
|
|
4539
|
+
const state = app.interactionSystem.getState ? app.interactionSystem.getState() : {};
|
|
4540
|
+
return { success: true, action: 'get_state', ...state };
|
|
4541
|
+
})();`.trim();
|
|
4542
|
+
default:
|
|
4543
|
+
return `(function() { return { error: 'Unknown interaction action: ${input.action}' }; })();`;
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
// ===========================================================================
|
|
4547
|
+
// WIDGET EXPORT
|
|
4548
|
+
// ===========================================================================
|
|
4549
|
+
generateExportWidget(input) {
|
|
4550
|
+
const opts = {};
|
|
4551
|
+
if (input.download !== undefined)
|
|
4552
|
+
opts.download = input.download;
|
|
4553
|
+
if (input.filename)
|
|
4554
|
+
opts.filename = input.filename;
|
|
4555
|
+
if (input.includeInteractions !== undefined)
|
|
4556
|
+
opts.includeInteractions = input.includeInteractions;
|
|
4557
|
+
if (input.minify !== undefined)
|
|
4558
|
+
opts.minify = input.minify;
|
|
4559
|
+
return `
|
|
4560
|
+
// Export widget (pp:PinePaper ontology JSON)
|
|
4561
|
+
(async function() {
|
|
4562
|
+
if (!app.exportWidget) return { error: 'Widget export not available' };
|
|
4563
|
+
const result = await app.exportWidget(${JSON.stringify(opts)});
|
|
4564
|
+
return { success: true, json: result.json, filename: result.filename, embedCode: result.embedCode, metadata: result.data?.metadata };
|
|
4565
|
+
})();`.trim();
|
|
4566
|
+
}
|
|
4567
|
+
generateExportWidgetHtml(input) {
|
|
4568
|
+
const opts = {};
|
|
4569
|
+
if (input.title)
|
|
4570
|
+
opts.title = input.title;
|
|
4571
|
+
if (input.download !== undefined)
|
|
4572
|
+
opts.download = input.download;
|
|
4573
|
+
return `
|
|
4574
|
+
// Export widget as self-contained HTML
|
|
4575
|
+
(async function() {
|
|
4576
|
+
if (!app.exportWidgetHTML) return { error: 'Widget HTML export not available' };
|
|
4577
|
+
const result = await app.exportWidgetHTML(${JSON.stringify(opts)});
|
|
4578
|
+
return { success: true, html: result.html, estimatedSize: result.estimatedSize, analysis: { itemTypes: [...result.analysis.itemTypes], relationTypes: [...result.analysis.relationTypes], hasSimpleAnimations: result.analysis.hasSimpleAnimations, hasKeyframeAnimations: result.analysis.hasKeyframeAnimations, hasMasks: result.analysis.hasMasks } };
|
|
4579
|
+
})();`.trim();
|
|
4580
|
+
}
|
|
4581
|
+
// ===========================================================================
|
|
4582
|
+
// DATA VISUALIZATION (CHARTS)
|
|
4583
|
+
// ===========================================================================
|
|
4584
|
+
generateChart(input) {
|
|
4585
|
+
const guard = `if (!app.chartSystem) return { error: 'ChartSystem not available' };`;
|
|
4586
|
+
switch (input.action) {
|
|
4587
|
+
case 'create': {
|
|
4588
|
+
if (!input.chartType)
|
|
4589
|
+
return `(function() { return { error: 'chartType is required for create' }; })();`;
|
|
4590
|
+
if (!input.data)
|
|
4591
|
+
return `(function() { return { error: 'data is required for create' }; })();`;
|
|
4592
|
+
const dataStr = JSON.stringify(input.data);
|
|
4593
|
+
const optsStr = JSON.stringify(input.options || {});
|
|
4594
|
+
return `
|
|
4595
|
+
// Create chart
|
|
4596
|
+
(function() {
|
|
4597
|
+
${guard}
|
|
4598
|
+
const group = app.createChart(${JSON.stringify(input.chartType)}, ${dataStr}, ${optsStr});
|
|
4599
|
+
if (!group) return { error: 'Failed to create chart' };
|
|
4600
|
+
const chartId = app.chartSystem.getLastChartId();
|
|
4601
|
+
return { success: true, action: 'create', chartType: ${JSON.stringify(input.chartType)}, chartId, dataPoints: ${input.data.length} };
|
|
4602
|
+
})();`.trim();
|
|
4603
|
+
}
|
|
4604
|
+
case 'update': {
|
|
4605
|
+
if (!input.chartId)
|
|
4606
|
+
return `(function() { return { error: 'chartId is required for update' }; })();`;
|
|
4607
|
+
const chartIdStr = JSON.stringify(input.chartId);
|
|
4608
|
+
const dataStr = JSON.stringify(input.data || []);
|
|
4609
|
+
const optsStr = JSON.stringify(input.options || {});
|
|
4610
|
+
return `
|
|
4611
|
+
// Update chart data
|
|
4612
|
+
(function() {
|
|
4613
|
+
${guard}
|
|
4614
|
+
const chart = app.chartSystem.getChart(${chartIdStr});
|
|
4615
|
+
if (!chart) return { error: 'Chart not found: ' + ${chartIdStr} };
|
|
4616
|
+
app.chartSystem.update(${chartIdStr}, ${dataStr}, ${optsStr});
|
|
4617
|
+
return { success: true, action: 'update', chartId: ${chartIdStr} };
|
|
4618
|
+
})();`.trim();
|
|
4619
|
+
}
|
|
4620
|
+
case 'reconfigure': {
|
|
4621
|
+
if (!input.chartId)
|
|
4622
|
+
return `(function() { return { error: 'chartId is required for reconfigure' }; })();`;
|
|
4623
|
+
const chartIdStr = JSON.stringify(input.chartId);
|
|
4624
|
+
const optsStr = JSON.stringify(input.options || {});
|
|
4625
|
+
return `
|
|
4626
|
+
// Reconfigure chart styling
|
|
4627
|
+
(function() {
|
|
4628
|
+
${guard}
|
|
4629
|
+
const chart = app.chartSystem.getChart(${chartIdStr});
|
|
4630
|
+
if (!chart) return { error: 'Chart not found: ' + ${chartIdStr} };
|
|
4631
|
+
app.chartSystem.reconfigure(${chartIdStr}, ${optsStr});
|
|
4632
|
+
return { success: true, action: 'reconfigure', chartId: ${chartIdStr} };
|
|
4633
|
+
})();`.trim();
|
|
4634
|
+
}
|
|
4635
|
+
case 'remove': {
|
|
4636
|
+
if (!input.chartId)
|
|
4637
|
+
return `(function() { return { error: 'chartId is required for remove' }; })();`;
|
|
4638
|
+
const chartIdStr = JSON.stringify(input.chartId);
|
|
4639
|
+
return `
|
|
4640
|
+
// Remove chart
|
|
4641
|
+
(function() {
|
|
4642
|
+
${guard}
|
|
4643
|
+
app.chartSystem.remove(${chartIdStr});
|
|
4644
|
+
return { success: true, action: 'remove', chartId: ${chartIdStr} };
|
|
4645
|
+
})();`.trim();
|
|
4646
|
+
}
|
|
4647
|
+
default:
|
|
4648
|
+
return `(function() { return { error: 'Unknown chart action: ${input.action}' }; })();`;
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
// ===========================================================================
|
|
4652
|
+
// MAGIC SYSTEM
|
|
4653
|
+
// ===========================================================================
|
|
4654
|
+
generateMagic(input) {
|
|
4655
|
+
const guard = `if (!app.magicSystem) return { error: 'MagicSystem not available' };`;
|
|
4656
|
+
switch (input.action) {
|
|
4657
|
+
case 'animate': {
|
|
4658
|
+
const opts = {};
|
|
4659
|
+
if (input.mood)
|
|
4660
|
+
opts.energy = input.mood;
|
|
4661
|
+
if (input.selectionOnly !== undefined)
|
|
4662
|
+
opts.selectionOnly = input.selectionOnly;
|
|
4663
|
+
return `
|
|
4664
|
+
// Magic auto-animate
|
|
4665
|
+
(async function() {
|
|
4666
|
+
${guard}
|
|
4667
|
+
const result = await app.magicSystem.autoAnimate(${JSON.stringify(opts)});
|
|
4668
|
+
return { success: true, action: 'animate', itemsAnimated: result.count, mood: result.energy, type: result.type };
|
|
4669
|
+
})();`.trim();
|
|
4670
|
+
}
|
|
4671
|
+
case 'remix': {
|
|
4672
|
+
const opts = {};
|
|
4673
|
+
if (input.selectionOnly !== undefined)
|
|
4674
|
+
opts.selectionOnly = input.selectionOnly;
|
|
4675
|
+
return `
|
|
4676
|
+
// Magic style remix
|
|
4677
|
+
(async function() {
|
|
4678
|
+
${guard}
|
|
4679
|
+
const result = await app.magicSystem.remixStyle(${JSON.stringify(opts)});
|
|
4680
|
+
return { success: true, action: 'remix', itemsRemixed: result.count, palette: result.palette };
|
|
4681
|
+
})();`.trim();
|
|
4682
|
+
}
|
|
4683
|
+
default:
|
|
4684
|
+
return `(function() { return { error: 'Unknown magic action: ${input.action}' }; })();`;
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
// ===========================================================================
|
|
4688
|
+
// PHYSICS (RIGID BODY SIMULATION)
|
|
4689
|
+
// ===========================================================================
|
|
4690
|
+
generatePhysics(input) {
|
|
4691
|
+
const guard = `if (!app.physicsWorld) return { error: 'PhysicsWorld not available' };`;
|
|
4692
|
+
switch (input.action) {
|
|
4693
|
+
case 'init': {
|
|
4694
|
+
const gravity = JSON.stringify(input.gravity || { x: 0, y: 980 });
|
|
4695
|
+
return `
|
|
4696
|
+
// Initialize physics world
|
|
4697
|
+
(function() {
|
|
4698
|
+
${guard}
|
|
4699
|
+
app.physicsWorld.init({ gravity: ${gravity} });
|
|
4700
|
+
return { success: true, action: 'init', gravity: ${gravity} };
|
|
4701
|
+
})();`.trim();
|
|
4702
|
+
}
|
|
4703
|
+
case 'add_body': {
|
|
4704
|
+
if (!input.itemId)
|
|
4705
|
+
return `(function() { return { error: 'itemId is required for add_body' }; })();`;
|
|
4706
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4707
|
+
const config = {};
|
|
4708
|
+
if (input.bodyType)
|
|
4709
|
+
config.type = input.bodyType;
|
|
4710
|
+
if (input.mass !== undefined)
|
|
4711
|
+
config.mass = input.mass;
|
|
4712
|
+
if (input.friction !== undefined)
|
|
4713
|
+
config.friction = input.friction;
|
|
4714
|
+
if (input.restitution !== undefined)
|
|
4715
|
+
config.restitution = input.restitution;
|
|
4716
|
+
if (input.fixedRotation !== undefined)
|
|
4717
|
+
config.fixedRotation = input.fixedRotation;
|
|
4718
|
+
if (input.shape)
|
|
4719
|
+
config.shape = input.shape;
|
|
4720
|
+
return `
|
|
4721
|
+
// Add physics body
|
|
4722
|
+
(function() {
|
|
4723
|
+
${guard}
|
|
4724
|
+
const entry = app.itemRegistry.get(${itemIdStr});
|
|
4725
|
+
if (!entry || !entry.item) return { error: 'Item not found: ' + ${itemIdStr} };
|
|
4726
|
+
const ok = app.physicsWorld.addBody(${itemIdStr}, ${JSON.stringify(config)});
|
|
4727
|
+
return { success: ok, action: 'add_body', itemId: ${itemIdStr}, bodyType: ${JSON.stringify(input.bodyType || 'dynamic')} };
|
|
4728
|
+
})();`.trim();
|
|
4729
|
+
}
|
|
4730
|
+
case 'remove_body': {
|
|
4731
|
+
if (!input.itemId)
|
|
4732
|
+
return `(function() { return { error: 'itemId is required for remove_body' }; })();`;
|
|
4733
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4734
|
+
return `
|
|
4735
|
+
// Remove physics body
|
|
4736
|
+
(function() {
|
|
4737
|
+
${guard}
|
|
4738
|
+
app.physicsWorld.removeBody(${itemIdStr});
|
|
4739
|
+
return { success: true, action: 'remove_body', itemId: ${itemIdStr} };
|
|
4740
|
+
})();`.trim();
|
|
4741
|
+
}
|
|
4742
|
+
case 'apply_force': {
|
|
4743
|
+
if (!input.itemId)
|
|
4744
|
+
return `(function() { return { error: 'itemId is required for apply_force' }; })();`;
|
|
4745
|
+
if (!input.force)
|
|
4746
|
+
return `(function() { return { error: 'force is required for apply_force' }; })();`;
|
|
4747
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4748
|
+
return `
|
|
4749
|
+
// Apply continuous force
|
|
4750
|
+
(function() {
|
|
4751
|
+
${guard}
|
|
4752
|
+
app.physicsWorld.applyForce(${itemIdStr}, ${JSON.stringify(input.force)});
|
|
4753
|
+
return { success: true, action: 'apply_force', itemId: ${itemIdStr}, force: ${JSON.stringify(input.force)} };
|
|
4754
|
+
})();`.trim();
|
|
4755
|
+
}
|
|
4756
|
+
case 'apply_impulse': {
|
|
4757
|
+
if (!input.itemId)
|
|
4758
|
+
return `(function() { return { error: 'itemId is required for apply_impulse' }; })();`;
|
|
4759
|
+
if (!input.impulse)
|
|
4760
|
+
return `(function() { return { error: 'impulse is required for apply_impulse' }; })();`;
|
|
4761
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4762
|
+
return `
|
|
4763
|
+
// Apply instant impulse
|
|
4764
|
+
(function() {
|
|
4765
|
+
${guard}
|
|
4766
|
+
app.physicsWorld.applyImpulse(${itemIdStr}, ${JSON.stringify(input.impulse)});
|
|
4767
|
+
return { success: true, action: 'apply_impulse', itemId: ${itemIdStr}, impulse: ${JSON.stringify(input.impulse)} };
|
|
4768
|
+
})();`.trim();
|
|
4769
|
+
}
|
|
4770
|
+
case 'set_velocity': {
|
|
4771
|
+
if (!input.itemId)
|
|
4772
|
+
return `(function() { return { error: 'itemId is required for set_velocity' }; })();`;
|
|
4773
|
+
if (!input.velocity)
|
|
4774
|
+
return `(function() { return { error: 'velocity is required for set_velocity' }; })();`;
|
|
4775
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4776
|
+
return `
|
|
4777
|
+
// Set body velocity
|
|
4778
|
+
(function() {
|
|
4779
|
+
${guard}
|
|
4780
|
+
app.physicsWorld.setVelocity(${itemIdStr}, ${JSON.stringify(input.velocity)});
|
|
4781
|
+
return { success: true, action: 'set_velocity', itemId: ${itemIdStr}, velocity: ${JSON.stringify(input.velocity)} };
|
|
4782
|
+
})();`.trim();
|
|
4783
|
+
}
|
|
4784
|
+
case 'get_state': {
|
|
4785
|
+
if (!input.itemId)
|
|
4786
|
+
return `(function() { return { error: 'itemId is required for get_state' }; })();`;
|
|
4787
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4788
|
+
return `
|
|
4789
|
+
// Get physics body state
|
|
4790
|
+
(function() {
|
|
4791
|
+
${guard}
|
|
4792
|
+
const state = app.physicsWorld.getBodyState(${itemIdStr});
|
|
4793
|
+
if (!state) return { error: 'No physics body for item: ' + ${itemIdStr} };
|
|
4794
|
+
return { success: true, action: 'get_state', itemId: ${itemIdStr}, ...state };
|
|
4795
|
+
})();`.trim();
|
|
4796
|
+
}
|
|
4797
|
+
case 'create_ground': {
|
|
4798
|
+
const y = input.y !== undefined ? input.y : 500;
|
|
4799
|
+
const w = input.width !== undefined ? input.width : 2000;
|
|
4800
|
+
return `
|
|
4801
|
+
// Create ground plane
|
|
4802
|
+
(function() {
|
|
4803
|
+
${guard}
|
|
4804
|
+
const groundId = app.physicsWorld.createGround(${y}, ${w});
|
|
4805
|
+
return { success: true, action: 'create_ground', groundId, y: ${y}, width: ${w} };
|
|
4806
|
+
})();`.trim();
|
|
4807
|
+
}
|
|
4808
|
+
case 'create_joint': {
|
|
4809
|
+
if (!input.itemId)
|
|
4810
|
+
return `(function() { return { error: 'itemId is required for create_joint' }; })();`;
|
|
4811
|
+
if (!input.targetItemId)
|
|
4812
|
+
return `(function() { return { error: 'targetItemId is required for create_joint' }; })();`;
|
|
4813
|
+
if (!input.jointType)
|
|
4814
|
+
return `(function() { return { error: 'jointType is required for create_joint' }; })();`;
|
|
4815
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4816
|
+
const targetStr = JSON.stringify(input.targetItemId);
|
|
4817
|
+
const params = JSON.stringify({ type: input.jointType, ...(input.jointParams || {}) });
|
|
4818
|
+
return `
|
|
4819
|
+
// Create physics joint
|
|
4820
|
+
(function() {
|
|
4821
|
+
${guard}
|
|
4822
|
+
const joint = app.physicsWorld.createJoint(${itemIdStr}, ${targetStr}, ${params});
|
|
4823
|
+
if (!joint) return { error: 'Failed to create joint between ' + ${itemIdStr} + ' and ' + ${targetStr} };
|
|
4824
|
+
return { success: true, action: 'create_joint', itemId: ${itemIdStr}, targetItemId: ${targetStr}, jointType: ${JSON.stringify(input.jointType)} };
|
|
4825
|
+
})();`.trim();
|
|
4826
|
+
}
|
|
4827
|
+
default:
|
|
4828
|
+
return `(function() { return { error: 'Unknown physics action: ${input.action}' }; })();`;
|
|
4829
|
+
}
|
|
4830
|
+
}
|
|
4831
|
+
// ===========================================================================
|
|
4832
|
+
// MEASUREMENT SYSTEM
|
|
4833
|
+
// ===========================================================================
|
|
4834
|
+
generateMeasurement(input) {
|
|
4835
|
+
const guard = `if (!app.measurementSystem) return { error: 'MeasurementSystem not available' };`;
|
|
4836
|
+
switch (input.action) {
|
|
4837
|
+
case 'set_rulers': {
|
|
4838
|
+
const enabled = input.enabled !== false;
|
|
4839
|
+
return `
|
|
4840
|
+
// Toggle rulers
|
|
4841
|
+
(function() {
|
|
4842
|
+
${guard}
|
|
4843
|
+
app.measurementSystem.setRulersVisible(${enabled});
|
|
4844
|
+
return { success: true, action: 'set_rulers', enabled: ${enabled} };
|
|
4845
|
+
})();`.trim();
|
|
4846
|
+
}
|
|
4847
|
+
case 'set_grid': {
|
|
4848
|
+
const enabled = input.enabled !== false;
|
|
4849
|
+
return `
|
|
4850
|
+
// Toggle grid
|
|
4851
|
+
(function() {
|
|
4852
|
+
${guard}
|
|
4853
|
+
app.measurementSystem.setGridVisible(${enabled});
|
|
4854
|
+
return { success: true, action: 'set_grid', enabled: ${enabled} };
|
|
4855
|
+
})();`.trim();
|
|
4856
|
+
}
|
|
4857
|
+
// No measurementSystem guard — reads from itemRegistry directly
|
|
4858
|
+
case 'get_dimensions': {
|
|
4859
|
+
if (!input.itemId)
|
|
4860
|
+
return `(function() { return { error: 'itemId is required for get_dimensions' }; })();`;
|
|
4861
|
+
const itemIdStr = JSON.stringify(input.itemId);
|
|
4862
|
+
return `
|
|
4863
|
+
// Get item dimensions
|
|
4864
|
+
(function() {
|
|
4865
|
+
const entry = app.itemRegistry.get(${itemIdStr});
|
|
4866
|
+
if (!entry || !entry.item) return { error: 'Item not found: ' + ${itemIdStr} };
|
|
4867
|
+
const b = entry.item.bounds;
|
|
4868
|
+
return { success: true, action: 'get_dimensions', itemId: ${itemIdStr}, x: b.x, y: b.y, width: b.width, height: b.height, rotation: entry.item.rotation || 0 };
|
|
4869
|
+
})();`.trim();
|
|
4870
|
+
}
|
|
4871
|
+
case 'set_snap': {
|
|
4872
|
+
const enabled = input.enabled !== false;
|
|
4873
|
+
return `
|
|
4874
|
+
// Toggle snap to grid
|
|
4875
|
+
(function() {
|
|
4876
|
+
${guard}
|
|
4877
|
+
app.measurementSystem.setSnapToUnitEnabled(${enabled});
|
|
4878
|
+
return { success: true, action: 'set_snap', enabled: ${enabled} };
|
|
4879
|
+
})();`.trim();
|
|
4880
|
+
}
|
|
4881
|
+
default:
|
|
4882
|
+
return `(function() { return { error: 'Unknown measurement action: ${input.action}' }; })();`;
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
3468
4885
|
}
|
|
3469
4886
|
// Export singleton instance
|
|
3470
4887
|
export const codeGenerator = new PinePaperCodeGenerator();
|