@pinepaper.studio/mcp-server 1.4.3 → 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 +114 -0
- package/dist/__tests__/fixtures/mock-generators.d.ts +147 -0
- package/dist/__tests__/fixtures/mock-generators.d.ts.map +1 -1
- package/dist/__tests__/fixtures/mock-generators.js +85 -0
- package/dist/__tests__/fixtures/mock-generators.js.map +1 -1
- package/dist/agent/export-manager.d.ts +77 -0
- package/dist/agent/export-manager.d.ts.map +1 -0
- package/dist/agent/export-manager.js +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/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 +4844 -1559
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +915 -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 +197 -1
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +2338 -26
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +5294 -1134
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +886 -4
- package/dist/types/schemas.js.map +1 -1
- package/manifest.json +70 -2
- package/package.json +1 -1
package/dist/tools/handlers.js
CHANGED
|
@@ -8,11 +8,35 @@
|
|
|
8
8
|
import { codeGenerator } from '../types/code-generator.js';
|
|
9
9
|
import { CreateItemInputSchema, ModifyItemInputSchema, DeleteItemInputSchema, AddRelationInputSchema, RemoveRelationInputSchema, QueryRelationsInputSchema, AnimateItemInputSchema, KeyframeAnimateInputSchema, ExecuteGeneratorInputSchema, ApplyEffectInputSchema, GetItemsInputSchema, PlayTimelineInputSchema, SetBackgroundColorInputSchema, SetCanvasSizeInputSchema, ExportTrainingDataInputSchema, ImportSVGInputSchema, AddFilterInputSchema, CreateGlossySphereInputSchema, CreateDiagonalStripesInputSchema, BatchCreateInputSchema, BatchModifyInputSchema, CreateGridInputSchema, CreateSceneInputSchema, GetPerformanceMetricsInputSchema, SearchAssetsInputSchema, ImportAssetInputSchema, P5DrawInputSchema,
|
|
10
10
|
// Diagram schemas
|
|
11
|
-
CreateDiagramShapeInputSchema, ConnectInputSchema, ConnectPortsInputSchema, AddPortsInputSchema, AutoLayoutInputSchema, GetDiagramShapesInputSchema, UpdateConnectorInputSchema, RemoveConnectorInputSchema, DiagramModeInputSchema,
|
|
11
|
+
CreateDiagramShapeInputSchema, ConnectInputSchema, ConnectPortsInputSchema, AddPortsInputSchema, AutoLayoutInputSchema, GetDiagramShapesInputSchema, UpdateConnectorInputSchema, RemoveConnectorInputSchema, DiagramModeInputSchema,
|
|
12
|
+
// Agent flow mode schemas
|
|
13
|
+
AgentStartJobInputSchema, AgentEndJobInputSchema, AgentResetInputSchema, AgentBatchExecuteInputSchema, AgentExportInputSchema, AgentAnalyzeInputSchema,
|
|
14
|
+
// Interactive/Trigger schemas
|
|
15
|
+
AddTriggerInputSchema, RemoveTriggerInputSchema, QueryTriggersInputSchema,
|
|
16
|
+
// Quiz schemas
|
|
17
|
+
CreateQuizInputSchema, GetQuizStateInputSchema, ResetQuizInputSchema,
|
|
18
|
+
// Letter collage schemas
|
|
19
|
+
CreateLetterCollageInputSchema, AnimateLetterCollageInputSchema,
|
|
20
|
+
// Map schemas
|
|
21
|
+
LoadMapInputSchema, HighlightRegionsInputSchema, UnhighlightRegionsInputSchema, ApplyDataColorsInputSchema, AddMarkerInputSchema, AddMapLabelsInputSchema, PanMapInputSchema, ZoomMapInputSchema, ImportCustomMapInputSchema, GetRegionAtPointInputSchema,
|
|
22
|
+
// Map animation/CSV schemas
|
|
23
|
+
AnimateMapRegionsInputSchema, AnimateMapWaveInputSchema, StopMapAnimationsInputSchema, ExportMapRegionCSVInputSchema, ImportMapRegionCSVInputSchema, SelectMapRegionsInputSchema, DeselectMapRegionsInputSchema,
|
|
24
|
+
// Custom relation/code schemas
|
|
25
|
+
RegisterCustomRelationInputSchema, ExecuteCustomCodeInputSchema,
|
|
26
|
+
// Mask schemas
|
|
27
|
+
ApplyAnimatedMaskInputSchema, ApplyCustomMaskInputSchema, RemoveMaskInputSchema,
|
|
28
|
+
// Template schemas
|
|
29
|
+
ApplyTemplateInputSchema,
|
|
30
|
+
// Image import schemas
|
|
31
|
+
ImportImageInputSchema, ErrorCodes, } from '../types/schemas.js';
|
|
12
32
|
import { ZodError } from 'zod';
|
|
33
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
34
|
+
import { tmpdir } from 'node:os';
|
|
35
|
+
import { join } from 'node:path';
|
|
13
36
|
import { getBrowserController, } from '../browser/puppeteer-controller.js';
|
|
14
37
|
import { getPerformanceTracker } from '../metrics/index.js';
|
|
15
38
|
import { formatErrorContext, captureCanvasState } from '../execution/index.js';
|
|
39
|
+
import { getSessionManager } from '../agent/session-manager.js';
|
|
16
40
|
/**
|
|
17
41
|
* Get screenshot mode from environment variable.
|
|
18
42
|
* Defaults to 'on_request' for optimal performance.
|
|
@@ -25,6 +49,37 @@ export function getScreenshotMode() {
|
|
|
25
49
|
// Default to 'on_request' for better performance (best practice)
|
|
26
50
|
return 'on_request';
|
|
27
51
|
}
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// EXPORT FILE SAVE HELPERS
|
|
54
|
+
// =============================================================================
|
|
55
|
+
function getExportDir() {
|
|
56
|
+
return process.env.PINEPAPER_EXPORT_DIR || join(tmpdir(), 'pinepaper-exports');
|
|
57
|
+
}
|
|
58
|
+
const ALWAYS_SAVE_FORMATS = new Set(['mp4', 'webm', 'gif', 'pdf']);
|
|
59
|
+
const SAVE_THRESHOLD_BYTES = 500_000; // ~500KB base64 ≈ 375KB decoded
|
|
60
|
+
function getFileExtension(format) {
|
|
61
|
+
const extMap = { mp4: 'mp4', webm: 'webm', gif: 'gif', pdf: 'pdf', png: 'png', svg: 'svg' };
|
|
62
|
+
return extMap[format] || format;
|
|
63
|
+
}
|
|
64
|
+
async function saveExportToFile(data, format, platform) {
|
|
65
|
+
const exportDir = getExportDir();
|
|
66
|
+
await mkdir(exportDir, { recursive: true });
|
|
67
|
+
const ext = getFileExtension(format);
|
|
68
|
+
const timestamp = Date.now();
|
|
69
|
+
const fileName = `pinepaper_${platform}_${timestamp}.${ext}`;
|
|
70
|
+
const filePath = join(exportDir, fileName);
|
|
71
|
+
if (data.startsWith('data:')) {
|
|
72
|
+
const base64Data = data.split(',')[1];
|
|
73
|
+
const buffer = Buffer.from(base64Data, 'base64');
|
|
74
|
+
await writeFile(filePath, buffer);
|
|
75
|
+
return { filePath, fileSize: buffer.length };
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Plain text (SVG)
|
|
79
|
+
await writeFile(filePath, data, 'utf-8');
|
|
80
|
+
return { filePath, fileSize: Buffer.byteLength(data, 'utf-8') };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
28
83
|
/**
|
|
29
84
|
* Get execution mode from environment variable.
|
|
30
85
|
* Defaults to 'puppeteer' for live execution.
|
|
@@ -153,7 +208,32 @@ async function executeOrGenerate(code, description, options, toolName) {
|
|
|
153
208
|
success: true,
|
|
154
209
|
metadata: { executionMode: 'code' },
|
|
155
210
|
});
|
|
156
|
-
return
|
|
211
|
+
return {
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: 'text',
|
|
215
|
+
text: `✅ **Code Generated Successfully**
|
|
216
|
+
|
|
217
|
+
**Tool:** ${toolName}
|
|
218
|
+
**Action:** ${description}
|
|
219
|
+
|
|
220
|
+
## Generated Code
|
|
221
|
+
|
|
222
|
+
\`\`\`javascript
|
|
223
|
+
${code}
|
|
224
|
+
\`\`\`
|
|
225
|
+
|
|
226
|
+
## How to Execute
|
|
227
|
+
|
|
228
|
+
1. Open **PinePaper Studio**: https://pinepaper.studio/editor?agent=1&mode=agent
|
|
229
|
+
2. Click the **Code Console** button (</> icon) in the toolbar
|
|
230
|
+
3. Paste the code above into the console
|
|
231
|
+
4. Click **Run** to execute
|
|
232
|
+
|
|
233
|
+
The code is ready to use. Each subsequent tool call will also generate code you can paste.`,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
};
|
|
157
237
|
}
|
|
158
238
|
if (!executeInBrowser) {
|
|
159
239
|
const totalDuration = tracker.endTimer(`${timerId}_total`);
|
|
@@ -167,17 +247,72 @@ async function executeOrGenerate(code, description, options, toolName) {
|
|
|
167
247
|
return successResult(code, description);
|
|
168
248
|
}
|
|
169
249
|
const controller = browserController || getBrowserController();
|
|
250
|
+
// Auto-connect in agent mode if not connected
|
|
170
251
|
if (!controller.connected) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
252
|
+
console.error('[PinePaper] Auto-connecting browser in agent mode...');
|
|
253
|
+
try {
|
|
254
|
+
await controller.connect(); // Will use connectAgent() due to enforced agentMode
|
|
255
|
+
console.error('[PinePaper] Browser auto-connected successfully');
|
|
256
|
+
}
|
|
257
|
+
catch (connectError) {
|
|
258
|
+
const totalDuration = tracker.endTimer(`${timerId}_total`);
|
|
259
|
+
tracker.recordMetric({
|
|
260
|
+
toolName,
|
|
261
|
+
phase: 'total',
|
|
262
|
+
duration: totalDuration,
|
|
263
|
+
timestamp: Date.now(),
|
|
264
|
+
success: false,
|
|
265
|
+
metadata: { browserConnected: false, autoConnectFailed: true, fallbackToCode: true },
|
|
266
|
+
});
|
|
267
|
+
// FALLBACK: Return code for manual paste instead of failing
|
|
268
|
+
const errorMessage = connectError instanceof Error ? connectError.message : 'Unknown error';
|
|
269
|
+
console.error(`[PinePaper] Browser connection failed: ${errorMessage}. Falling back to code-only mode.`);
|
|
270
|
+
return {
|
|
271
|
+
content: [
|
|
272
|
+
{
|
|
273
|
+
type: 'text',
|
|
274
|
+
text: `⚠️ **Browser Connection Failed** - Falling back to code mode
|
|
275
|
+
|
|
276
|
+
**Error:** ${errorMessage}
|
|
277
|
+
|
|
278
|
+
**Don't worry!** You can still use PinePaper by copying the code below and pasting it into the PinePaper Code Console.
|
|
279
|
+
|
|
280
|
+
## How to use the code manually:
|
|
281
|
+
|
|
282
|
+
1. Open **PinePaper Studio**: https://pinepaper.studio/editor?agent=1&mode=agent
|
|
283
|
+
2. Click the **Code Console** button (</> icon) in the toolbar
|
|
284
|
+
3. Paste the code below into the console
|
|
285
|
+
4. Click **Run** to execute
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Generated Code for: ${toolName}
|
|
290
|
+
|
|
291
|
+
\`\`\`javascript
|
|
292
|
+
${code}
|
|
293
|
+
\`\`\`
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
**${description}**
|
|
298
|
+
|
|
299
|
+
💡 **Tip:** If browser connection keeps failing, check:
|
|
300
|
+
- Is Chrome/Chromium installed?
|
|
301
|
+
- Are there firewall restrictions?
|
|
302
|
+
- Try setting \`headless: false\` in browser_connect to see the browser window`,
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Auto-start agent session if not already active (enforced agent mode)
|
|
309
|
+
const sessionManager = getSessionManager();
|
|
310
|
+
if (!sessionManager.hasActiveJob()) {
|
|
311
|
+
sessionManager.startJob({
|
|
312
|
+
name: 'auto_session',
|
|
313
|
+
screenshotPolicy: 'on_complete',
|
|
179
314
|
});
|
|
180
|
-
|
|
315
|
+
console.error('[PinePaper] Auto-started agent session for enforced agent mode');
|
|
181
316
|
}
|
|
182
317
|
// Track browser execution time
|
|
183
318
|
tracker.startTimer(`${timerId}_browser_execution`);
|
|
@@ -318,6 +453,14 @@ export async function handleToolCall(toolName, args, options = {}) {
|
|
|
318
453
|
}
|
|
319
454
|
case 'pinepaper_modify_item': {
|
|
320
455
|
const input = ModifyItemInputSchema.parse(args);
|
|
456
|
+
// Guard: reject animation properties — they don't work via modify
|
|
457
|
+
const animProps = ['animationType', 'animationSpeed', 'keyframes', 'animation'];
|
|
458
|
+
const passedAnimProps = animProps.filter(p => p in (input.properties || {}));
|
|
459
|
+
if (passedAnimProps.length > 0) {
|
|
460
|
+
return errorResult('INVALID_PROPERTIES', `Animation properties (${passedAnimProps.join(', ')}) cannot be set via pinepaper_modify_item — they will be silently ignored. ` +
|
|
461
|
+
`Use pinepaper_animate for loop animations (pulse, rotate, bounce, fade, wobble, slide) ` +
|
|
462
|
+
`or pinepaper_keyframe_animate for timed/sequenced animations with precise control.`);
|
|
463
|
+
}
|
|
321
464
|
const code = codeGenerator.generateModifyItem(input);
|
|
322
465
|
const description = getLocalizedSuccessMessage(i18n, 'itemModified', {
|
|
323
466
|
itemId: input.itemId,
|
|
@@ -386,7 +529,7 @@ export async function handleToolCall(toolName, args, options = {}) {
|
|
|
386
529
|
const description = getLocalizedSuccessMessage(i18n, 'relationAdded', {
|
|
387
530
|
relationType: input.relationType,
|
|
388
531
|
sourceId: input.sourceId,
|
|
389
|
-
targetId: input.targetId,
|
|
532
|
+
targetId: input.targetId || 'self',
|
|
390
533
|
});
|
|
391
534
|
return executeOrGenerate(code, description, options, 'pinepaper_add_relation');
|
|
392
535
|
}
|
|
@@ -404,6 +547,16 @@ export async function handleToolCall(toolName, args, options = {}) {
|
|
|
404
547
|
const code = codeGenerator.generateQueryRelations(input.itemId, input.relationType, input.direction);
|
|
405
548
|
return executeOrGenerate(code, `Queries ${input.direction || 'all'} relations for ${input.itemId}`, options, 'pinepaper_query_relations');
|
|
406
549
|
}
|
|
550
|
+
case 'pinepaper_register_custom_relation': {
|
|
551
|
+
const input = RegisterCustomRelationInputSchema.parse(args);
|
|
552
|
+
const code = codeGenerator.generateRegisterCustomRelation(input);
|
|
553
|
+
return executeOrGenerate(code, `Registers custom relation type: ${input.name}`, options, 'pinepaper_register_custom_relation');
|
|
554
|
+
}
|
|
555
|
+
case 'pinepaper_execute_custom_code': {
|
|
556
|
+
const input = ExecuteCustomCodeInputSchema.parse(args);
|
|
557
|
+
const code = codeGenerator.generateExecuteCustomCode(input);
|
|
558
|
+
return executeOrGenerate(code, input.description || 'Executes custom JavaScript code', options, 'pinepaper_execute_custom_code');
|
|
559
|
+
}
|
|
407
560
|
// -----------------------------------------------------------------------
|
|
408
561
|
// ANIMATION TOOLS
|
|
409
562
|
// -----------------------------------------------------------------------
|
|
@@ -454,6 +607,204 @@ export async function handleToolCall(toolName, args, options = {}) {
|
|
|
454
607
|
return executeOrGenerate(code, description, options, 'pinepaper_apply_effect');
|
|
455
608
|
}
|
|
456
609
|
// -----------------------------------------------------------------------
|
|
610
|
+
// MASK TOOLS
|
|
611
|
+
// -----------------------------------------------------------------------
|
|
612
|
+
case 'pinepaper_apply_animated_mask': {
|
|
613
|
+
const input = ApplyAnimatedMaskInputSchema.parse(args);
|
|
614
|
+
const code = codeGenerator.generateApplyAnimatedMask(input);
|
|
615
|
+
const mode = input.preset && input.keyframes ? 'hybrid' : input.preset ? 'preset' : 'custom';
|
|
616
|
+
return executeOrGenerate(code, `Applies ${mode} animated mask to ${input.itemId}`, options, 'pinepaper_apply_animated_mask');
|
|
617
|
+
}
|
|
618
|
+
case 'pinepaper_apply_custom_mask': {
|
|
619
|
+
const input = ApplyCustomMaskInputSchema.parse(args);
|
|
620
|
+
const code = codeGenerator.generateApplyCustomMask(input);
|
|
621
|
+
return executeOrGenerate(code, `Applies custom ${input.maskType} mask with ${input.keyframes.length} keyframes to ${input.itemId}`, options, 'pinepaper_apply_custom_mask');
|
|
622
|
+
}
|
|
623
|
+
case 'pinepaper_remove_mask': {
|
|
624
|
+
const input = RemoveMaskInputSchema.parse(args);
|
|
625
|
+
const code = codeGenerator.generateRemoveMask(input);
|
|
626
|
+
return executeOrGenerate(code, `Removes mask from ${input.itemId}`, options, 'pinepaper_remove_mask');
|
|
627
|
+
}
|
|
628
|
+
case 'pinepaper_get_animatable_properties': {
|
|
629
|
+
const code = codeGenerator.generateGetAnimatableProperties();
|
|
630
|
+
return executeOrGenerate(code, 'Gets animatable properties for mask types', options, 'pinepaper_get_animatable_properties');
|
|
631
|
+
}
|
|
632
|
+
case 'pinepaper_get_available_easings': {
|
|
633
|
+
const code = codeGenerator.generateGetAvailableEasings();
|
|
634
|
+
return executeOrGenerate(code, 'Gets available easing functions', options, 'pinepaper_get_available_easings');
|
|
635
|
+
}
|
|
636
|
+
case 'pinepaper_get_mask_types': {
|
|
637
|
+
const code = codeGenerator.generateGetMaskTypes();
|
|
638
|
+
return executeOrGenerate(code, 'Gets available mask types', options, 'pinepaper_get_mask_types');
|
|
639
|
+
}
|
|
640
|
+
case 'pinepaper_get_mask_animations': {
|
|
641
|
+
const code = codeGenerator.generateGetMaskAnimations();
|
|
642
|
+
return executeOrGenerate(code, 'Gets available mask animation presets', options, 'pinepaper_get_mask_animations');
|
|
643
|
+
}
|
|
644
|
+
// -----------------------------------------------------------------------
|
|
645
|
+
// CAMERA TOOLS
|
|
646
|
+
// -----------------------------------------------------------------------
|
|
647
|
+
case 'pinepaper_camera_animate': {
|
|
648
|
+
const keyframes = args.keyframes;
|
|
649
|
+
const duration = args.duration;
|
|
650
|
+
const loop = args.loop ?? false;
|
|
651
|
+
const delay = args.delay ?? 0;
|
|
652
|
+
const keyframesStr = JSON.stringify(keyframes);
|
|
653
|
+
const code = `app.camera && app.camera.animate ? app.camera.animate(${keyframesStr}, ${duration}, ${loop}, ${delay}) : app.addRelation('camera', 'camera', 'camera_animates', { keyframes: ${keyframesStr}, duration: ${duration}, loop: ${loop}, delay: ${delay} });`;
|
|
654
|
+
return executeOrGenerate(code, `Animates camera with ${keyframes.length} keyframes over ${duration}s`, options, 'pinepaper_camera_animate');
|
|
655
|
+
}
|
|
656
|
+
case 'pinepaper_camera_zoom': {
|
|
657
|
+
const direction = args.direction;
|
|
658
|
+
const level = args.level ?? (direction === 'in' ? 2 : 0.5);
|
|
659
|
+
const duration = args.duration ?? 0.5;
|
|
660
|
+
const method = direction === 'in' ? 'zoomIn' : 'zoomOut';
|
|
661
|
+
const code = `app.camera && app.camera.${method} ? app.camera.${method}(${level}, ${duration}) : null;`;
|
|
662
|
+
return executeOrGenerate(code, `Camera zoom ${direction} to ${level}x`, options, 'pinepaper_camera_zoom');
|
|
663
|
+
}
|
|
664
|
+
case 'pinepaper_camera_pan': {
|
|
665
|
+
const direction = args.direction;
|
|
666
|
+
const amount = args.amount ?? 100;
|
|
667
|
+
const x = args.x;
|
|
668
|
+
const y = args.y;
|
|
669
|
+
const duration = args.duration ?? 0.5;
|
|
670
|
+
let code;
|
|
671
|
+
let description;
|
|
672
|
+
if (x !== undefined && y !== undefined) {
|
|
673
|
+
code = `app.camera && app.camera.panTo ? app.camera.panTo(${x}, ${y}, ${duration}) : null;`;
|
|
674
|
+
description = `Camera pan to (${x}, ${y})`;
|
|
675
|
+
}
|
|
676
|
+
else if (direction) {
|
|
677
|
+
const methodMap = { left: 'panLeft', right: 'panRight', up: 'panUp', down: 'panDown' };
|
|
678
|
+
const method = methodMap[direction];
|
|
679
|
+
code = `app.camera && app.camera.${method} ? app.camera.${method}(${amount}, ${duration}) : null;`;
|
|
680
|
+
description = `Camera pan ${direction} by ${amount}px`;
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
code = `// No direction or coordinates specified`;
|
|
684
|
+
description = 'Camera pan (no parameters)';
|
|
685
|
+
}
|
|
686
|
+
return executeOrGenerate(code, description, options, 'pinepaper_camera_pan');
|
|
687
|
+
}
|
|
688
|
+
case 'pinepaper_camera_move_to': {
|
|
689
|
+
const x = args.x;
|
|
690
|
+
const y = args.y;
|
|
691
|
+
const zoom = args.zoom;
|
|
692
|
+
const duration = args.duration ?? 0.5;
|
|
693
|
+
const code = `app.camera && app.camera.moveTo ? app.camera.moveTo(${x}, ${y}, ${zoom}, ${duration}) : null;`;
|
|
694
|
+
return executeOrGenerate(code, `Camera move to (${x}, ${y}) at ${zoom}x zoom`, options, 'pinepaper_camera_move_to');
|
|
695
|
+
}
|
|
696
|
+
case 'pinepaper_camera_reset': {
|
|
697
|
+
const duration = args.duration ?? 0.5;
|
|
698
|
+
const code = `app.camera && app.camera.reset ? app.camera.reset(${duration}) : null;`;
|
|
699
|
+
return executeOrGenerate(code, 'Reset camera to default state', options, 'pinepaper_camera_reset');
|
|
700
|
+
}
|
|
701
|
+
case 'pinepaper_camera_stop': {
|
|
702
|
+
const code = `app.camera && app.camera.stop ? app.camera.stop() : null;`;
|
|
703
|
+
return executeOrGenerate(code, 'Stop camera animation', options, 'pinepaper_camera_stop');
|
|
704
|
+
}
|
|
705
|
+
case 'pinepaper_camera_state': {
|
|
706
|
+
const code = `app.camera && app.camera.getState ? app.camera.getState() : { zoom: 1, center: [400, 300], isAnimating: false };`;
|
|
707
|
+
return executeOrGenerate(code, 'Get current camera state', options, 'pinepaper_camera_state');
|
|
708
|
+
}
|
|
709
|
+
// -----------------------------------------------------------------------
|
|
710
|
+
// FONT TOOLS
|
|
711
|
+
// -----------------------------------------------------------------------
|
|
712
|
+
case 'pinepaper_font_show_studio': {
|
|
713
|
+
const code = `app.fontStudio && app.fontStudio.show ? app.fontStudio.show() : null;`;
|
|
714
|
+
return executeOrGenerate(code, 'Opens Font Studio UI', options, 'pinepaper_font_show_studio');
|
|
715
|
+
}
|
|
716
|
+
case 'pinepaper_font_set_name': {
|
|
717
|
+
const { name } = args;
|
|
718
|
+
const code = `app.fontStudio.setName(${JSON.stringify(name)});`;
|
|
719
|
+
return executeOrGenerate(code, `Set font name to "${name}"`, options, 'pinepaper_font_set_name');
|
|
720
|
+
}
|
|
721
|
+
case 'pinepaper_font_get_required_chars': {
|
|
722
|
+
const { set } = args;
|
|
723
|
+
const setArg = set ? JSON.stringify(set) : '"minimum"';
|
|
724
|
+
const code = `app.fontStudio.getRequiredChars(${setArg});`;
|
|
725
|
+
return executeOrGenerate(code, `Get required characters (${set || 'minimum'} set)`, options, 'pinepaper_font_get_required_chars');
|
|
726
|
+
}
|
|
727
|
+
case 'pinepaper_font_get_status': {
|
|
728
|
+
const code = `app.fontStudio.getStatus();`;
|
|
729
|
+
return executeOrGenerate(code, 'Get font completion status', options, 'pinepaper_font_get_status');
|
|
730
|
+
}
|
|
731
|
+
case 'pinepaper_font_create_glyph': {
|
|
732
|
+
const { character, pathId } = args;
|
|
733
|
+
const code = `app.fontStudio.createGlyph(${JSON.stringify(character)}, ${JSON.stringify(pathId)});`;
|
|
734
|
+
return executeOrGenerate(code, `Create glyph for "${character}" from path ${pathId}`, options, 'pinepaper_font_create_glyph');
|
|
735
|
+
}
|
|
736
|
+
case 'pinepaper_font_create_space': {
|
|
737
|
+
const { width } = args;
|
|
738
|
+
const code = width !== undefined
|
|
739
|
+
? `app.fontStudio.createSpace(${width});`
|
|
740
|
+
: `app.fontStudio.createSpace();`;
|
|
741
|
+
return executeOrGenerate(code, `Create space glyph${width ? ` (width: ${width})` : ''}`, options, 'pinepaper_font_create_space');
|
|
742
|
+
}
|
|
743
|
+
case 'pinepaper_font_remove_glyph': {
|
|
744
|
+
const { character } = args;
|
|
745
|
+
const code = `app.fontStudio.removeGlyph(${JSON.stringify(character)});`;
|
|
746
|
+
return executeOrGenerate(code, `Remove glyph for "${character}"`, options, 'pinepaper_font_remove_glyph');
|
|
747
|
+
}
|
|
748
|
+
case 'pinepaper_font_set_metrics': {
|
|
749
|
+
const metrics = args;
|
|
750
|
+
const code = `app.fontStudio.setMetrics(${JSON.stringify(metrics)});`;
|
|
751
|
+
return executeOrGenerate(code, 'Set font metrics', options, 'pinepaper_font_set_metrics');
|
|
752
|
+
}
|
|
753
|
+
case 'pinepaper_font_export': {
|
|
754
|
+
const { download } = args;
|
|
755
|
+
const code = download === false
|
|
756
|
+
? `app.fontStudio.export({ download: false });`
|
|
757
|
+
: `app.fontStudio.export();`;
|
|
758
|
+
return executeOrGenerate(code, 'Export font as OTF', options, 'pinepaper_font_export');
|
|
759
|
+
}
|
|
760
|
+
case 'pinepaper_font_load_into_document': {
|
|
761
|
+
const code = `app.fontStudio.loadIntoDocument();`;
|
|
762
|
+
return executeOrGenerate(code, 'Load font into document', options, 'pinepaper_font_load_into_document');
|
|
763
|
+
}
|
|
764
|
+
case 'pinepaper_font_export_data': {
|
|
765
|
+
const { download } = args;
|
|
766
|
+
const code = download === false
|
|
767
|
+
? `app.fontStudio.exportData({ download: false });`
|
|
768
|
+
: `app.fontStudio.exportData();`;
|
|
769
|
+
return executeOrGenerate(code, 'Export font data as JSON', options, 'pinepaper_font_export_data');
|
|
770
|
+
}
|
|
771
|
+
case 'pinepaper_font_import_data': {
|
|
772
|
+
const { data } = args;
|
|
773
|
+
const code = `app.fontStudio.importData(${JSON.stringify(data)});`;
|
|
774
|
+
return executeOrGenerate(code, 'Import font data from JSON', options, 'pinepaper_font_import_data');
|
|
775
|
+
}
|
|
776
|
+
case 'pinepaper_font_clear': {
|
|
777
|
+
const code = `app.fontStudio.clear();`;
|
|
778
|
+
return executeOrGenerate(code, 'Clear all glyphs and reset font', options, 'pinepaper_font_clear');
|
|
779
|
+
}
|
|
780
|
+
case 'pinepaper_font_remove_overlap': {
|
|
781
|
+
const { pathId } = args;
|
|
782
|
+
const code = `app.fontStudio.removeOverlap(${JSON.stringify(pathId)});`;
|
|
783
|
+
return executeOrGenerate(code, `Remove overlaps from path ${pathId}`, options, 'pinepaper_font_remove_overlap');
|
|
784
|
+
}
|
|
785
|
+
case 'pinepaper_font_correct_direction': {
|
|
786
|
+
const { pathId } = args;
|
|
787
|
+
const code = `app.fontStudio.correctDirection(${JSON.stringify(pathId)});`;
|
|
788
|
+
return executeOrGenerate(code, `Correct path direction for ${pathId}`, options, 'pinepaper_font_correct_direction');
|
|
789
|
+
}
|
|
790
|
+
case 'pinepaper_font_cleanup_path': {
|
|
791
|
+
const { pathId, removeOverlap, correctDirection, smooth, smoothTolerance } = args;
|
|
792
|
+
const optsObj = {};
|
|
793
|
+
if (removeOverlap !== undefined)
|
|
794
|
+
optsObj.removeOverlap = removeOverlap;
|
|
795
|
+
if (correctDirection !== undefined)
|
|
796
|
+
optsObj.correctDirection = correctDirection;
|
|
797
|
+
if (smooth !== undefined)
|
|
798
|
+
optsObj.smooth = smooth;
|
|
799
|
+
if (smoothTolerance !== undefined)
|
|
800
|
+
optsObj.smoothTolerance = smoothTolerance;
|
|
801
|
+
const hasOpts = Object.keys(optsObj).length > 0;
|
|
802
|
+
const code = hasOpts
|
|
803
|
+
? `app.fontStudio.cleanupPath(${JSON.stringify(pathId)}, ${JSON.stringify(optsObj)});`
|
|
804
|
+
: `app.fontStudio.cleanupPath(${JSON.stringify(pathId)});`;
|
|
805
|
+
return executeOrGenerate(code, `Cleanup path ${pathId}`, options, 'pinepaper_font_cleanup_path');
|
|
806
|
+
}
|
|
807
|
+
// -----------------------------------------------------------------------
|
|
457
808
|
// QUERY TOOLS
|
|
458
809
|
// -----------------------------------------------------------------------
|
|
459
810
|
case 'pinepaper_get_items': {
|
|
@@ -565,19 +916,24 @@ You can now start creating new items on a clean canvas.`,
|
|
|
565
916
|
});
|
|
566
917
|
return executeOrGenerate(code, description, options, 'pinepaper_export_training_data');
|
|
567
918
|
}
|
|
919
|
+
case 'pinepaper_export_scene': {
|
|
920
|
+
const code = codeGenerator.generateExportScene();
|
|
921
|
+
return executeOrGenerate(code, 'Exports complete scene state', options, 'pinepaper_export_scene');
|
|
922
|
+
}
|
|
568
923
|
// -----------------------------------------------------------------------
|
|
569
924
|
// BROWSER CONTROL TOOLS
|
|
570
925
|
// -----------------------------------------------------------------------
|
|
571
926
|
case 'pinepaper_browser_connect': {
|
|
572
927
|
const url = args.url || 'https://pinepaper.studio';
|
|
573
|
-
|
|
928
|
+
// Default to headless: true for enforced agent mode
|
|
929
|
+
const headless = args.headless ?? true;
|
|
574
930
|
const controller = getBrowserController({ studioUrl: url, headless });
|
|
575
931
|
if (controller.connected) {
|
|
576
932
|
return {
|
|
577
933
|
content: [
|
|
578
934
|
{
|
|
579
935
|
type: 'text',
|
|
580
|
-
text: `Already connected to PinePaper Studio at ${controller.
|
|
936
|
+
text: `Already connected to PinePaper Studio at ${controller.actualUrl}. Do NOT call this tool again — proceed directly with pinepaper_agent_start_job.`,
|
|
581
937
|
},
|
|
582
938
|
],
|
|
583
939
|
};
|
|
@@ -593,8 +949,9 @@ You can now start creating new items on a clean canvas.`,
|
|
|
593
949
|
const content = [
|
|
594
950
|
{
|
|
595
951
|
type: 'text',
|
|
596
|
-
text: `✅ Connected to PinePaper Studio at ${
|
|
952
|
+
text: `✅ Connected to PinePaper Studio at ${controller.actualUrl}
|
|
597
953
|
|
|
954
|
+
**Agent mode enabled** (agent=1&mode=agent parameters added automatically)
|
|
598
955
|
**Current canvas size: ${canvasSize.width}x${canvasSize.height}**
|
|
599
956
|
|
|
600
957
|
⚠️ IMPORTANT NOTES:
|
|
@@ -615,7 +972,39 @@ Browser is ready. You can now use other pinepaper tools to create and animate gr
|
|
|
615
972
|
return { content };
|
|
616
973
|
}
|
|
617
974
|
catch (error) {
|
|
618
|
-
|
|
975
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to connect to browser';
|
|
976
|
+
// Provide helpful fallback instructions instead of just failing
|
|
977
|
+
return {
|
|
978
|
+
content: [
|
|
979
|
+
{
|
|
980
|
+
type: 'text',
|
|
981
|
+
text: `⚠️ **Browser Connection Failed**
|
|
982
|
+
|
|
983
|
+
**Error:** ${errorMessage}
|
|
984
|
+
|
|
985
|
+
## Don't worry! You can still use PinePaper manually:
|
|
986
|
+
|
|
987
|
+
### Option 1: Manual Browser Mode
|
|
988
|
+
1. Open **PinePaper Studio** in your browser: https://pinepaper.studio/editor?agent=1&mode=agent
|
|
989
|
+
2. Continue using PinePaper tools - the MCP server will generate code for each action
|
|
990
|
+
3. Copy the generated code and paste it into the **Code Console** (</> icon in toolbar)
|
|
991
|
+
|
|
992
|
+
### Option 2: Troubleshoot Browser Connection
|
|
993
|
+
- **Check Chrome/Chromium**: Puppeteer requires Chrome or Chromium installed
|
|
994
|
+
- **Try headless: false**: Call \`pinepaper_browser_connect\` with \`headless: false\` to see the browser window
|
|
995
|
+
- **Check permissions**: Ensure the process has permission to launch browsers
|
|
996
|
+
- **Firewall**: Check if firewall is blocking browser launch
|
|
997
|
+
|
|
998
|
+
### What happens now?
|
|
999
|
+
All PinePaper tools will work in **code-only mode**:
|
|
1000
|
+
- Each tool generates JavaScript code
|
|
1001
|
+
- You copy the code and paste it in PinePaper's Code Console
|
|
1002
|
+
- The code executes and creates your graphics
|
|
1003
|
+
|
|
1004
|
+
**The MCP server is fully functional** - just without automatic browser control.`,
|
|
1005
|
+
},
|
|
1006
|
+
],
|
|
1007
|
+
};
|
|
619
1008
|
}
|
|
620
1009
|
}
|
|
621
1010
|
case 'pinepaper_browser_disconnect': {
|
|
@@ -664,7 +1053,8 @@ Browser is ready. You can now use other pinepaper tools to create and animate gr
|
|
|
664
1053
|
type: 'text',
|
|
665
1054
|
text: JSON.stringify({
|
|
666
1055
|
connected: controller.connected,
|
|
667
|
-
studioUrl: controller.
|
|
1056
|
+
studioUrl: controller.actualUrl,
|
|
1057
|
+
agentMode: controller.agentMode,
|
|
668
1058
|
}, null, 2),
|
|
669
1059
|
},
|
|
670
1060
|
],
|
|
@@ -894,6 +1284,426 @@ Browser is ready. You can now use other pinepaper tools to create and animate gr
|
|
|
894
1284
|
return executeOrGenerate(code, description, options, 'pinepaper_diagram_mode');
|
|
895
1285
|
}
|
|
896
1286
|
// -----------------------------------------------------------------------
|
|
1287
|
+
// AGENT FLOW MODE TOOLS
|
|
1288
|
+
// -----------------------------------------------------------------------
|
|
1289
|
+
case 'pinepaper_agent_start_job': {
|
|
1290
|
+
const input = AgentStartJobInputSchema.parse(args);
|
|
1291
|
+
// Guard against pipeline restarts — warn loudly if a job is already active
|
|
1292
|
+
const session = getSessionManager();
|
|
1293
|
+
if (session.hasActiveJob()) {
|
|
1294
|
+
const currentJob = session.getCurrentJob();
|
|
1295
|
+
const itemCount = currentJob?.itemsCreated.length ?? 0;
|
|
1296
|
+
if (itemCount > 0) {
|
|
1297
|
+
// Job has items — this is almost certainly a pipeline restart bug
|
|
1298
|
+
console.error(`[PinePaper] WARNING: Starting new job while ${itemCount} items exist from active job ${currentJob?.jobId}`);
|
|
1299
|
+
const code = codeGenerator.generateAgentStartJob(input);
|
|
1300
|
+
const result = await executeOrGenerate(code, `Restarted job (previous had ${itemCount} items)`, options, 'pinepaper_agent_start_job');
|
|
1301
|
+
// Prepend a strong warning to the result text
|
|
1302
|
+
if (result.content && result.content.length > 0 && result.content[0].type === 'text') {
|
|
1303
|
+
result.content[0].text = `⚠️ WARNING: A job was already active with ${itemCount} items created. Starting a new job will create DUPLICATE items on the canvas. If you already created items, do NOT recreate them — continue from where you left off or call pinepaper_agent_end_job instead.\n\n${result.content[0].text}`;
|
|
1304
|
+
}
|
|
1305
|
+
return result;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
const code = codeGenerator.generateAgentStartJob(input);
|
|
1309
|
+
const description = `Started agent job${input.name ? ` "${input.name}"` : ''} with ${input.screenshotPolicy || 'on_complete'} screenshot policy`;
|
|
1310
|
+
return executeOrGenerate(code, description, options, 'pinepaper_agent_start_job');
|
|
1311
|
+
}
|
|
1312
|
+
case 'pinepaper_agent_end_job': {
|
|
1313
|
+
const input = AgentEndJobInputSchema.parse(args);
|
|
1314
|
+
const code = codeGenerator.generateAgentEndJob(input);
|
|
1315
|
+
const description = 'Ended agent job with summary and recommendations';
|
|
1316
|
+
// In code mode or non-browser mode, return as usual
|
|
1317
|
+
const endJobExecMode = options.executionMode ?? getExecutionMode();
|
|
1318
|
+
if (endJobExecMode === 'code' || !options.executeInBrowser) {
|
|
1319
|
+
return executeOrGenerate(code, description, options, 'pinepaper_agent_end_job');
|
|
1320
|
+
}
|
|
1321
|
+
// Execute in browser and intercept result to save large screenshots to disk
|
|
1322
|
+
const endJobController = options.browserController || getBrowserController();
|
|
1323
|
+
if (!endJobController.connected) {
|
|
1324
|
+
return executeOrGenerate(code, description, options, 'pinepaper_agent_end_job');
|
|
1325
|
+
}
|
|
1326
|
+
// Auto-start agent session if not active
|
|
1327
|
+
const endJobSessionMgr = getSessionManager();
|
|
1328
|
+
if (!endJobSessionMgr.hasActiveJob()) {
|
|
1329
|
+
endJobSessionMgr.startJob({ name: 'auto_session', screenshotPolicy: 'on_complete' });
|
|
1330
|
+
}
|
|
1331
|
+
const endJobBrowserResult = await endJobController.executeCode(code, false);
|
|
1332
|
+
if (!endJobBrowserResult.success) {
|
|
1333
|
+
const canvasState = await captureCanvasState(endJobController);
|
|
1334
|
+
return errorResult(ErrorCodes.EXECUTION_ERROR, endJobBrowserResult.error || 'End job failed', { code }, { toolName: 'pinepaper_agent_end_job', canvasState: canvasState || undefined });
|
|
1335
|
+
}
|
|
1336
|
+
const endJobResult = endJobBrowserResult.result;
|
|
1337
|
+
// Check if result contains a large screenshot data URL
|
|
1338
|
+
if (endJobResult?.screenshot && typeof endJobResult.screenshot === 'string' && endJobResult.screenshot.length > SAVE_THRESHOLD_BYTES) {
|
|
1339
|
+
try {
|
|
1340
|
+
const { filePath, fileSize } = await saveExportToFile(endJobResult.screenshot, 'png', 'screenshot');
|
|
1341
|
+
const cleanResult = { ...endJobResult, screenshot: undefined, screenshotPath: filePath, screenshotSize: fileSize };
|
|
1342
|
+
// Take a smaller Puppeteer viewport screenshot to show the user inline
|
|
1343
|
+
const previewScreenshot = await endJobController.takeScreenshot();
|
|
1344
|
+
const content = [{
|
|
1345
|
+
type: 'text',
|
|
1346
|
+
text: `Executed PinePaper code:\n\n\`\`\`javascript\n${code}\n\`\`\`\n\nFull screenshot saved to: ${filePath} (${(fileSize / 1024).toFixed(1)} KB)\n\nResult: ${JSON.stringify(cleanResult, null, 2)}`,
|
|
1347
|
+
}];
|
|
1348
|
+
if (previewScreenshot) {
|
|
1349
|
+
content.push({
|
|
1350
|
+
type: 'image',
|
|
1351
|
+
data: previewScreenshot,
|
|
1352
|
+
mimeType: 'image/png',
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
return { content };
|
|
1356
|
+
}
|
|
1357
|
+
catch (saveError) {
|
|
1358
|
+
console.error('[PinePaper] Failed to save end_job screenshot to file:', saveError);
|
|
1359
|
+
// Fall through to inline result but strip the screenshot to avoid oversized response
|
|
1360
|
+
const strippedResult = { ...endJobResult, screenshot: '[screenshot too large for inline — save failed]' };
|
|
1361
|
+
return executedResult(code, strippedResult, undefined, description);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
return executedResult(code, endJobResult, endJobBrowserResult.screenshot, description);
|
|
1365
|
+
}
|
|
1366
|
+
case 'pinepaper_agent_reset': {
|
|
1367
|
+
const input = AgentResetInputSchema.parse(args);
|
|
1368
|
+
const code = codeGenerator.generateAgentReset(input);
|
|
1369
|
+
const description = `Fast canvas reset${input.canvasPreset ? ` to ${input.canvasPreset} preset` : ''}`;
|
|
1370
|
+
return executeOrGenerate(code, description, options, 'pinepaper_agent_reset');
|
|
1371
|
+
}
|
|
1372
|
+
case 'pinepaper_agent_batch_execute': {
|
|
1373
|
+
const input = AgentBatchExecuteInputSchema.parse(args);
|
|
1374
|
+
const code = codeGenerator.generateAgentBatchExecute(input);
|
|
1375
|
+
const description = `Batch executed ${input.operations.length} operations${input.atomic !== false ? ' (atomic)' : ''}`;
|
|
1376
|
+
// Check for out-of-bounds items before execution
|
|
1377
|
+
let boundsWarning = '';
|
|
1378
|
+
const sizeOp = input.operations.find((op) => op.type === 'set_canvas_size');
|
|
1379
|
+
if (sizeOp) {
|
|
1380
|
+
const cw = sizeOp.width || 1920;
|
|
1381
|
+
const ch = sizeOp.height || 1080;
|
|
1382
|
+
const oobItems = [];
|
|
1383
|
+
input.operations.forEach((op, idx) => {
|
|
1384
|
+
if (op.type === 'create' && op.position) {
|
|
1385
|
+
const { x, y } = op.position;
|
|
1386
|
+
if (x < 0 || y < 0 || x > cw || y > ch) {
|
|
1387
|
+
oobItems.push(`op[${idx}] ${op.itemType || 'item'} at (${x},${y})`);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
if (oobItems.length > 0) {
|
|
1392
|
+
boundsWarning = `\n\nWARNING: ${oobItems.length} item(s) positioned outside canvas bounds (${cw}x${ch}): ${oobItems.join(', ')}. Items may be clipped or invisible.`;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
const batchResult = await executeOrGenerate(code, description, options, 'pinepaper_agent_batch_execute');
|
|
1396
|
+
// Append bounds warning if any
|
|
1397
|
+
if (boundsWarning && batchResult.content && batchResult.content.length > 0) {
|
|
1398
|
+
const firstContent = batchResult.content[0];
|
|
1399
|
+
if (firstContent.type === 'text') {
|
|
1400
|
+
firstContent.text += boundsWarning;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
return batchResult;
|
|
1404
|
+
}
|
|
1405
|
+
case 'pinepaper_agent_export': {
|
|
1406
|
+
const input = AgentExportInputSchema.parse(args);
|
|
1407
|
+
const code = codeGenerator.generateAgentExport(input);
|
|
1408
|
+
const description = `Smart export for ${input.platform} as ${input.format || 'auto'}`;
|
|
1409
|
+
// In code mode, return generated code as usual
|
|
1410
|
+
const effectiveExecMode = options.executionMode ?? getExecutionMode();
|
|
1411
|
+
if (effectiveExecMode === 'code' || !options.executeInBrowser) {
|
|
1412
|
+
return executeOrGenerate(code, description, options, 'pinepaper_agent_export');
|
|
1413
|
+
}
|
|
1414
|
+
// Execute in browser and intercept result for file saving
|
|
1415
|
+
const controller = options.browserController || getBrowserController();
|
|
1416
|
+
// Auto-connect if needed
|
|
1417
|
+
if (!controller.connected) {
|
|
1418
|
+
console.error('[PinePaper] Auto-connecting browser for export...');
|
|
1419
|
+
try {
|
|
1420
|
+
await controller.connect();
|
|
1421
|
+
}
|
|
1422
|
+
catch (connectError) {
|
|
1423
|
+
// Fall back to executeOrGenerate which handles the fallback gracefully
|
|
1424
|
+
return executeOrGenerate(code, description, options, 'pinepaper_agent_export');
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
// Auto-start agent session if not active
|
|
1428
|
+
const exportSessionManager = getSessionManager();
|
|
1429
|
+
if (!exportSessionManager.hasActiveJob()) {
|
|
1430
|
+
exportSessionManager.startJob({ name: 'auto_session', screenshotPolicy: 'on_complete' });
|
|
1431
|
+
}
|
|
1432
|
+
const exportBrowserResult = await controller.executeCode(code, false);
|
|
1433
|
+
if (!exportBrowserResult.success) {
|
|
1434
|
+
const canvasState = await captureCanvasState(controller);
|
|
1435
|
+
return errorResult(ErrorCodes.EXECUTION_ERROR, exportBrowserResult.error || 'Export failed', { code }, { toolName: 'pinepaper_agent_export', canvasState: canvasState || undefined });
|
|
1436
|
+
}
|
|
1437
|
+
const exportResult = exportBrowserResult.result;
|
|
1438
|
+
const format = exportResult?.format || input.format || 'png';
|
|
1439
|
+
const data = exportResult?.data;
|
|
1440
|
+
const shouldSaveToFile = data && typeof data === 'string' && (ALWAYS_SAVE_FORMATS.has(format) ||
|
|
1441
|
+
data.length > SAVE_THRESHOLD_BYTES);
|
|
1442
|
+
if (shouldSaveToFile) {
|
|
1443
|
+
try {
|
|
1444
|
+
const { filePath, fileSize } = await saveExportToFile(data, format, input.platform || 'auto');
|
|
1445
|
+
const cleanResult = { ...exportResult, data: undefined, filePath, fileSize };
|
|
1446
|
+
return {
|
|
1447
|
+
content: [{
|
|
1448
|
+
type: 'text',
|
|
1449
|
+
text: `Export saved to file:\n\nFile: ${filePath}\nFormat: ${format}\nSize: ${(fileSize / 1024).toFixed(1)} KB\nPlatform: ${input.platform}\n\nResult: ${JSON.stringify(cleanResult, null, 2)}`,
|
|
1450
|
+
}],
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
catch (saveError) {
|
|
1454
|
+
console.error('[PinePaper] Failed to save export to file:', saveError);
|
|
1455
|
+
// Strip the massive data to prevent oversized response
|
|
1456
|
+
const strippedResult = { ...exportResult, data: `[${format} export data — file save failed: ${saveError instanceof Error ? saveError.message : 'unknown error'}]` };
|
|
1457
|
+
return executedResult(code, strippedResult, exportBrowserResult.screenshot, description);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
// Small export — return inline (existing behavior)
|
|
1461
|
+
return executedResult(code, exportResult, exportBrowserResult.screenshot, description);
|
|
1462
|
+
}
|
|
1463
|
+
case 'pinepaper_agent_analyze': {
|
|
1464
|
+
const input = AgentAnalyzeInputSchema.parse(args);
|
|
1465
|
+
const code = codeGenerator.generateAgentAnalyze(input);
|
|
1466
|
+
const description = 'Analyzed canvas content for export recommendations';
|
|
1467
|
+
return executeOrGenerate(code, description, options, 'pinepaper_agent_analyze');
|
|
1468
|
+
}
|
|
1469
|
+
// -----------------------------------------------------------------------
|
|
1470
|
+
// INTERACTIVE TRIGGER TOOLS
|
|
1471
|
+
// -----------------------------------------------------------------------
|
|
1472
|
+
case 'pinepaper_add_trigger': {
|
|
1473
|
+
const input = AddTriggerInputSchema.parse(args);
|
|
1474
|
+
const code = codeGenerator.generateAddTrigger(input);
|
|
1475
|
+
const description = `Added ${input.event} trigger to ${input.itemId} with ${input.actions.length} actions`;
|
|
1476
|
+
return executeOrGenerate(code, description, options, 'pinepaper_add_trigger');
|
|
1477
|
+
}
|
|
1478
|
+
case 'pinepaper_remove_trigger': {
|
|
1479
|
+
const input = RemoveTriggerInputSchema.parse(args);
|
|
1480
|
+
const code = codeGenerator.generateRemoveTrigger(input);
|
|
1481
|
+
const description = input.removeAll
|
|
1482
|
+
? `Removed all triggers from ${input.itemId}`
|
|
1483
|
+
: `Removed ${input.event} trigger from ${input.itemId}`;
|
|
1484
|
+
return executeOrGenerate(code, description, options, 'pinepaper_remove_trigger');
|
|
1485
|
+
}
|
|
1486
|
+
case 'pinepaper_query_triggers': {
|
|
1487
|
+
const input = QueryTriggersInputSchema.parse(args);
|
|
1488
|
+
const code = codeGenerator.generateQueryTriggers(input);
|
|
1489
|
+
const description = input.itemId
|
|
1490
|
+
? `Queried triggers for ${input.itemId}`
|
|
1491
|
+
: 'Queried all triggers on canvas';
|
|
1492
|
+
return executeOrGenerate(code, description, options, 'pinepaper_query_triggers');
|
|
1493
|
+
}
|
|
1494
|
+
// -----------------------------------------------------------------------
|
|
1495
|
+
// QUIZ/LMS TOOLS
|
|
1496
|
+
// -----------------------------------------------------------------------
|
|
1497
|
+
case 'pinepaper_create_quiz': {
|
|
1498
|
+
const input = CreateQuizInputSchema.parse(args);
|
|
1499
|
+
const code = codeGenerator.generateCreateQuiz(input);
|
|
1500
|
+
const description = `Created quiz${input.title ? ` "${input.title}"` : ''} with ${input.questions.length} questions`;
|
|
1501
|
+
return executeOrGenerate(code, description, options, 'pinepaper_create_quiz');
|
|
1502
|
+
}
|
|
1503
|
+
case 'pinepaper_get_quiz_state': {
|
|
1504
|
+
const input = GetQuizStateInputSchema.parse(args);
|
|
1505
|
+
const code = codeGenerator.generateGetQuizState(input);
|
|
1506
|
+
const description = input.quizId
|
|
1507
|
+
? `Got state for quiz ${input.quizId}`
|
|
1508
|
+
: 'Got state for active quiz';
|
|
1509
|
+
return executeOrGenerate(code, description, options, 'pinepaper_get_quiz_state');
|
|
1510
|
+
}
|
|
1511
|
+
case 'pinepaper_reset_quiz': {
|
|
1512
|
+
const input = ResetQuizInputSchema.parse(args);
|
|
1513
|
+
const code = codeGenerator.generateResetQuiz(input);
|
|
1514
|
+
const description = input.quizId
|
|
1515
|
+
? `Reset quiz ${input.quizId}`
|
|
1516
|
+
: 'Reset active quiz';
|
|
1517
|
+
return executeOrGenerate(code, description, options, 'pinepaper_reset_quiz');
|
|
1518
|
+
}
|
|
1519
|
+
// -----------------------------------------------------------------------
|
|
1520
|
+
// LETTER COLLAGE TOOLS
|
|
1521
|
+
// -----------------------------------------------------------------------
|
|
1522
|
+
case 'pinepaper_create_letter_collage': {
|
|
1523
|
+
const input = CreateLetterCollageInputSchema.parse(args);
|
|
1524
|
+
const code = codeGenerator.generateCreateLetterCollage(input);
|
|
1525
|
+
const description = `Created ${input.style || 'tile'} letter collage: "${input.text}"`;
|
|
1526
|
+
return executeOrGenerate(code, description, options, 'pinepaper_create_letter_collage');
|
|
1527
|
+
}
|
|
1528
|
+
case 'pinepaper_animate_letter_collage': {
|
|
1529
|
+
const input = AnimateLetterCollageInputSchema.parse(args);
|
|
1530
|
+
const code = codeGenerator.generateAnimateLetterCollage(input);
|
|
1531
|
+
const description = `Animated collage ${input.collageId} with ${input.animationType}`;
|
|
1532
|
+
return executeOrGenerate(code, description, options, 'pinepaper_animate_letter_collage');
|
|
1533
|
+
}
|
|
1534
|
+
case 'pinepaper_get_letter_collage_options': {
|
|
1535
|
+
const code = codeGenerator.generateGetLetterCollageOptions();
|
|
1536
|
+
return executeOrGenerate(code, 'Gets available letter collage styles and palettes', options, 'pinepaper_get_letter_collage_options');
|
|
1537
|
+
}
|
|
1538
|
+
// -----------------------------------------------------------------------
|
|
1539
|
+
// CANVAS PRESETS TOOLS
|
|
1540
|
+
// -----------------------------------------------------------------------
|
|
1541
|
+
case 'pinepaper_get_canvas_presets': {
|
|
1542
|
+
const code = codeGenerator.generateGetCanvasPresets();
|
|
1543
|
+
return executeOrGenerate(code, 'Gets all available canvas presets', options, 'pinepaper_get_canvas_presets');
|
|
1544
|
+
}
|
|
1545
|
+
// -----------------------------------------------------------------------
|
|
1546
|
+
// MAP TOOLS
|
|
1547
|
+
// -----------------------------------------------------------------------
|
|
1548
|
+
case 'pinepaper_load_map': {
|
|
1549
|
+
const input = LoadMapInputSchema.parse(args);
|
|
1550
|
+
const code = codeGenerator.generateLoadMap(input);
|
|
1551
|
+
const description = `Loads ${input.mapId} map with ${input.projection || 'default'} projection`;
|
|
1552
|
+
return executeOrGenerate(code, description, options, 'pinepaper_load_map');
|
|
1553
|
+
}
|
|
1554
|
+
case 'pinepaper_highlight_regions': {
|
|
1555
|
+
const input = HighlightRegionsInputSchema.parse(args);
|
|
1556
|
+
const code = codeGenerator.generateHighlightRegions(input);
|
|
1557
|
+
const description = `Highlights ${input.regionIds.length} region(s) on the map`;
|
|
1558
|
+
return executeOrGenerate(code, description, options, 'pinepaper_highlight_regions');
|
|
1559
|
+
}
|
|
1560
|
+
case 'pinepaper_unhighlight_regions': {
|
|
1561
|
+
const input = UnhighlightRegionsInputSchema.parse(args);
|
|
1562
|
+
const code = codeGenerator.generateUnhighlightRegions(input);
|
|
1563
|
+
return executeOrGenerate(code, 'Removes region highlights', options, 'pinepaper_unhighlight_regions');
|
|
1564
|
+
}
|
|
1565
|
+
case 'pinepaper_apply_data_colors': {
|
|
1566
|
+
const input = ApplyDataColorsInputSchema.parse(args);
|
|
1567
|
+
const code = codeGenerator.generateApplyDataColors(input);
|
|
1568
|
+
const regionCount = Object.keys(input.data).length;
|
|
1569
|
+
const description = `Applies choropleth coloring to ${regionCount} region(s)`;
|
|
1570
|
+
return executeOrGenerate(code, description, options, 'pinepaper_apply_data_colors');
|
|
1571
|
+
}
|
|
1572
|
+
case 'pinepaper_add_marker': {
|
|
1573
|
+
const input = AddMarkerInputSchema.parse(args);
|
|
1574
|
+
const code = codeGenerator.generateAddMarker(input);
|
|
1575
|
+
const description = `Adds marker at [${input.lat}, ${input.lon}]${input.label ? `: ${input.label}` : ''}`;
|
|
1576
|
+
return executeOrGenerate(code, description, options, 'pinepaper_add_marker');
|
|
1577
|
+
}
|
|
1578
|
+
case 'pinepaper_add_map_labels': {
|
|
1579
|
+
const input = AddMapLabelsInputSchema.parse(args);
|
|
1580
|
+
const code = codeGenerator.generateAddMapLabels(input);
|
|
1581
|
+
return executeOrGenerate(code, 'Adds labels to map regions', options, 'pinepaper_add_map_labels');
|
|
1582
|
+
}
|
|
1583
|
+
case 'pinepaper_pan_map': {
|
|
1584
|
+
const input = PanMapInputSchema.parse(args);
|
|
1585
|
+
const code = codeGenerator.generatePanMap(input);
|
|
1586
|
+
const description = `Pans map to [${input.lat}, ${input.lon}]`;
|
|
1587
|
+
return executeOrGenerate(code, description, options, 'pinepaper_pan_map');
|
|
1588
|
+
}
|
|
1589
|
+
case 'pinepaper_zoom_map': {
|
|
1590
|
+
const input = ZoomMapInputSchema.parse(args);
|
|
1591
|
+
const code = codeGenerator.generateZoomMap(input);
|
|
1592
|
+
const description = `Sets map zoom level to ${input.level}`;
|
|
1593
|
+
return executeOrGenerate(code, description, options, 'pinepaper_zoom_map');
|
|
1594
|
+
}
|
|
1595
|
+
case 'pinepaper_export_map': {
|
|
1596
|
+
const code = codeGenerator.generateExportMap();
|
|
1597
|
+
return executeOrGenerate(code, 'Exports map configuration', options, 'pinepaper_export_map');
|
|
1598
|
+
}
|
|
1599
|
+
case 'pinepaper_import_custom_map': {
|
|
1600
|
+
const input = ImportCustomMapInputSchema.parse(args);
|
|
1601
|
+
const code = codeGenerator.generateImportCustomMap(input);
|
|
1602
|
+
const description = input.url ? `Imports custom map from URL` : 'Imports custom GeoJSON map';
|
|
1603
|
+
return executeOrGenerate(code, description, options, 'pinepaper_import_custom_map');
|
|
1604
|
+
}
|
|
1605
|
+
case 'pinepaper_get_region_at_point': {
|
|
1606
|
+
const input = GetRegionAtPointInputSchema.parse(args);
|
|
1607
|
+
const code = codeGenerator.generateGetRegionAtPoint(input);
|
|
1608
|
+
const description = `Gets region at point [${input.x}, ${input.y}]`;
|
|
1609
|
+
return executeOrGenerate(code, description, options, 'pinepaper_get_region_at_point');
|
|
1610
|
+
}
|
|
1611
|
+
// -----------------------------------------------------------------------
|
|
1612
|
+
// MAP ANIMATION/CSV TOOLS
|
|
1613
|
+
// -----------------------------------------------------------------------
|
|
1614
|
+
case 'pinepaper_animate_map_regions': {
|
|
1615
|
+
const input = AnimateMapRegionsInputSchema.parse(args);
|
|
1616
|
+
const code = codeGenerator.generateAnimateMapRegions(input);
|
|
1617
|
+
const regionCount = Object.keys(input.regions).length;
|
|
1618
|
+
const description = `Animates ${regionCount} map region(s) over ${input.duration || 5}s`;
|
|
1619
|
+
return executeOrGenerate(code, description, options, 'pinepaper_animate_map_regions');
|
|
1620
|
+
}
|
|
1621
|
+
case 'pinepaper_animate_map_wave': {
|
|
1622
|
+
const input = AnimateMapWaveInputSchema.parse(args);
|
|
1623
|
+
const code = codeGenerator.generateAnimateMapWave(input);
|
|
1624
|
+
const description = `Creates ${input.waveDirection || 'horizontal'} wave animation`;
|
|
1625
|
+
return executeOrGenerate(code, description, options, 'pinepaper_animate_map_wave');
|
|
1626
|
+
}
|
|
1627
|
+
case 'pinepaper_stop_map_animations': {
|
|
1628
|
+
const input = StopMapAnimationsInputSchema.parse(args);
|
|
1629
|
+
const code = codeGenerator.generateStopMapAnimations(input);
|
|
1630
|
+
return executeOrGenerate(code, 'Stops map region animations', options, 'pinepaper_stop_map_animations');
|
|
1631
|
+
}
|
|
1632
|
+
case 'pinepaper_get_animated_map_regions': {
|
|
1633
|
+
const code = codeGenerator.generateGetAnimatedMapRegions();
|
|
1634
|
+
return executeOrGenerate(code, 'Gets list of animated map regions', options, 'pinepaper_get_animated_map_regions');
|
|
1635
|
+
}
|
|
1636
|
+
case 'pinepaper_export_map_region_csv': {
|
|
1637
|
+
const input = ExportMapRegionCSVInputSchema.parse(args);
|
|
1638
|
+
const code = codeGenerator.generateExportMapRegionCSV(input);
|
|
1639
|
+
return executeOrGenerate(code, 'Exports map region data as CSV', options, 'pinepaper_export_map_region_csv');
|
|
1640
|
+
}
|
|
1641
|
+
case 'pinepaper_import_map_region_csv': {
|
|
1642
|
+
const input = ImportMapRegionCSVInputSchema.parse(args);
|
|
1643
|
+
const code = codeGenerator.generateImportMapRegionCSV(input);
|
|
1644
|
+
return executeOrGenerate(code, 'Imports CSV data to update map regions', options, 'pinepaper_import_map_region_csv');
|
|
1645
|
+
}
|
|
1646
|
+
case 'pinepaper_select_map_regions': {
|
|
1647
|
+
const input = SelectMapRegionsInputSchema.parse(args);
|
|
1648
|
+
const code = codeGenerator.generateSelectMapRegions(input);
|
|
1649
|
+
const description = `Selects ${input.regionIds.length} map region(s)`;
|
|
1650
|
+
return executeOrGenerate(code, description, options, 'pinepaper_select_map_regions');
|
|
1651
|
+
}
|
|
1652
|
+
case 'pinepaper_deselect_map_regions': {
|
|
1653
|
+
const input = DeselectMapRegionsInputSchema.parse(args);
|
|
1654
|
+
const code = codeGenerator.generateDeselectMapRegions(input);
|
|
1655
|
+
return executeOrGenerate(code, 'Deselects map regions', options, 'pinepaper_deselect_map_regions');
|
|
1656
|
+
}
|
|
1657
|
+
case 'pinepaper_get_highlighted_map_regions': {
|
|
1658
|
+
const code = codeGenerator.generateGetHighlightedMapRegions();
|
|
1659
|
+
return executeOrGenerate(code, 'Gets list of highlighted map regions', options, 'pinepaper_get_highlighted_map_regions');
|
|
1660
|
+
}
|
|
1661
|
+
case 'pinepaper_export_map_geojson': {
|
|
1662
|
+
const code = codeGenerator.generateExportMapGeoJson({
|
|
1663
|
+
includeStyles: args.includeStyles,
|
|
1664
|
+
includeMetadata: args.includeMetadata,
|
|
1665
|
+
selectedOnly: args.selectedOnly,
|
|
1666
|
+
download: args.download,
|
|
1667
|
+
filename: args.filename,
|
|
1668
|
+
});
|
|
1669
|
+
return executeOrGenerate(code, 'Exports map as GeoJSON', options, 'pinepaper_export_map_geojson');
|
|
1670
|
+
}
|
|
1671
|
+
case 'pinepaper_export_original_map_geojson': {
|
|
1672
|
+
const code = codeGenerator.generateExportOriginalMapGeoJson({
|
|
1673
|
+
download: args.download,
|
|
1674
|
+
filename: args.filename,
|
|
1675
|
+
});
|
|
1676
|
+
return executeOrGenerate(code, 'Exports original map GeoJSON', options, 'pinepaper_export_original_map_geojson');
|
|
1677
|
+
}
|
|
1678
|
+
case 'pinepaper_get_map_source_info': {
|
|
1679
|
+
const code = codeGenerator.generateGetMapSourceInfo();
|
|
1680
|
+
return executeOrGenerate(code, 'Gets map source information', options, 'pinepaper_get_map_source_info');
|
|
1681
|
+
}
|
|
1682
|
+
case 'pinepaper_register_item': {
|
|
1683
|
+
const code = codeGenerator.generateRegisterItem({
|
|
1684
|
+
itemJson: args.itemJson,
|
|
1685
|
+
itemType: args.itemType,
|
|
1686
|
+
properties: args.properties,
|
|
1687
|
+
});
|
|
1688
|
+
return executeOrGenerate(code, 'Registers Paper.js item', options, 'pinepaper_register_item');
|
|
1689
|
+
}
|
|
1690
|
+
// -----------------------------------------------------------------------
|
|
1691
|
+
// TEMPLATE TOOLS
|
|
1692
|
+
// -----------------------------------------------------------------------
|
|
1693
|
+
case 'pinepaper_apply_template': {
|
|
1694
|
+
const parsed = ApplyTemplateInputSchema.parse(args);
|
|
1695
|
+
const code = codeGenerator.generateApplyTemplate(parsed);
|
|
1696
|
+
return executeOrGenerate(code, 'Applies template', options, 'pinepaper_apply_template');
|
|
1697
|
+
}
|
|
1698
|
+
// -----------------------------------------------------------------------
|
|
1699
|
+
// IMAGE IMPORT TOOLS
|
|
1700
|
+
// -----------------------------------------------------------------------
|
|
1701
|
+
case 'pinepaper_import_image': {
|
|
1702
|
+
const parsed = ImportImageInputSchema.parse(args);
|
|
1703
|
+
const code = codeGenerator.generateImportImage(parsed);
|
|
1704
|
+
return executeOrGenerate(code, 'Imports image', options, 'pinepaper_import_image');
|
|
1705
|
+
}
|
|
1706
|
+
// -----------------------------------------------------------------------
|
|
897
1707
|
// UNKNOWN TOOL
|
|
898
1708
|
// -----------------------------------------------------------------------
|
|
899
1709
|
default: {
|
|
@@ -913,12 +1723,28 @@ Browser is ready. You can now use other pinepaper tools to create and animate gr
|
|
|
913
1723
|
'pinepaper_add_relation',
|
|
914
1724
|
'pinepaper_remove_relation',
|
|
915
1725
|
'pinepaper_query_relations',
|
|
1726
|
+
'pinepaper_register_custom_relation',
|
|
1727
|
+
'pinepaper_execute_custom_code',
|
|
916
1728
|
'pinepaper_animate',
|
|
917
1729
|
'pinepaper_keyframe_animate',
|
|
918
1730
|
'pinepaper_play_timeline',
|
|
919
1731
|
'pinepaper_execute_generator',
|
|
920
1732
|
'pinepaper_list_generators',
|
|
921
1733
|
'pinepaper_apply_effect',
|
|
1734
|
+
'pinepaper_apply_animated_mask',
|
|
1735
|
+
'pinepaper_apply_custom_mask',
|
|
1736
|
+
'pinepaper_remove_mask',
|
|
1737
|
+
'pinepaper_get_animatable_properties',
|
|
1738
|
+
'pinepaper_get_available_easings',
|
|
1739
|
+
'pinepaper_get_mask_types',
|
|
1740
|
+
'pinepaper_get_mask_animations',
|
|
1741
|
+
'pinepaper_camera_animate',
|
|
1742
|
+
'pinepaper_camera_zoom',
|
|
1743
|
+
'pinepaper_camera_pan',
|
|
1744
|
+
'pinepaper_camera_move_to',
|
|
1745
|
+
'pinepaper_camera_reset',
|
|
1746
|
+
'pinepaper_camera_stop',
|
|
1747
|
+
'pinepaper_camera_state',
|
|
922
1748
|
'pinepaper_get_items',
|
|
923
1749
|
'pinepaper_get_relation_stats',
|
|
924
1750
|
'pinepaper_set_background_color',
|
|
@@ -930,6 +1756,7 @@ Browser is ready. You can now use other pinepaper tools to create and animate gr
|
|
|
930
1756
|
'pinepaper_add_filter',
|
|
931
1757
|
'pinepaper_export_svg',
|
|
932
1758
|
'pinepaper_export_training_data',
|
|
1759
|
+
'pinepaper_export_scene',
|
|
933
1760
|
'pinepaper_browser_connect',
|
|
934
1761
|
'pinepaper_browser_disconnect',
|
|
935
1762
|
'pinepaper_browser_screenshot',
|
|
@@ -948,6 +1775,77 @@ Browser is ready. You can now use other pinepaper tools to create and animate gr
|
|
|
948
1775
|
'pinepaper_update_connector',
|
|
949
1776
|
'pinepaper_remove_connector',
|
|
950
1777
|
'pinepaper_diagram_mode',
|
|
1778
|
+
// Scene tools
|
|
1779
|
+
'pinepaper_create_scene',
|
|
1780
|
+
// Agent flow mode tools
|
|
1781
|
+
'pinepaper_agent_start_job',
|
|
1782
|
+
'pinepaper_agent_end_job',
|
|
1783
|
+
'pinepaper_agent_reset',
|
|
1784
|
+
'pinepaper_agent_batch_execute',
|
|
1785
|
+
'pinepaper_agent_export',
|
|
1786
|
+
'pinepaper_agent_analyze',
|
|
1787
|
+
// Interactive trigger tools
|
|
1788
|
+
'pinepaper_add_trigger',
|
|
1789
|
+
'pinepaper_remove_trigger',
|
|
1790
|
+
'pinepaper_query_triggers',
|
|
1791
|
+
// Quiz/LMS tools
|
|
1792
|
+
'pinepaper_create_quiz',
|
|
1793
|
+
'pinepaper_get_quiz_state',
|
|
1794
|
+
'pinepaper_reset_quiz',
|
|
1795
|
+
// Letter collage tools
|
|
1796
|
+
'pinepaper_create_letter_collage',
|
|
1797
|
+
'pinepaper_animate_letter_collage',
|
|
1798
|
+
'pinepaper_get_letter_collage_options',
|
|
1799
|
+
// Canvas presets tools
|
|
1800
|
+
'pinepaper_get_canvas_presets',
|
|
1801
|
+
// Map tools
|
|
1802
|
+
'pinepaper_load_map',
|
|
1803
|
+
'pinepaper_highlight_regions',
|
|
1804
|
+
'pinepaper_unhighlight_regions',
|
|
1805
|
+
'pinepaper_apply_data_colors',
|
|
1806
|
+
'pinepaper_add_marker',
|
|
1807
|
+
'pinepaper_add_map_labels',
|
|
1808
|
+
'pinepaper_pan_map',
|
|
1809
|
+
'pinepaper_zoom_map',
|
|
1810
|
+
'pinepaper_export_map',
|
|
1811
|
+
'pinepaper_import_custom_map',
|
|
1812
|
+
'pinepaper_get_region_at_point',
|
|
1813
|
+
// Map animation/CSV tools
|
|
1814
|
+
'pinepaper_animate_map_regions',
|
|
1815
|
+
'pinepaper_animate_map_wave',
|
|
1816
|
+
'pinepaper_stop_map_animations',
|
|
1817
|
+
'pinepaper_get_animated_map_regions',
|
|
1818
|
+
'pinepaper_export_map_region_csv',
|
|
1819
|
+
'pinepaper_import_map_region_csv',
|
|
1820
|
+
'pinepaper_select_map_regions',
|
|
1821
|
+
'pinepaper_deselect_map_regions',
|
|
1822
|
+
'pinepaper_get_highlighted_map_regions',
|
|
1823
|
+
'pinepaper_export_map_geojson',
|
|
1824
|
+
'pinepaper_export_original_map_geojson',
|
|
1825
|
+
'pinepaper_get_map_source_info',
|
|
1826
|
+
// Font tools
|
|
1827
|
+
'pinepaper_font_show_studio',
|
|
1828
|
+
'pinepaper_font_set_name',
|
|
1829
|
+
'pinepaper_font_get_required_chars',
|
|
1830
|
+
'pinepaper_font_get_status',
|
|
1831
|
+
'pinepaper_font_create_glyph',
|
|
1832
|
+
'pinepaper_font_create_space',
|
|
1833
|
+
'pinepaper_font_remove_glyph',
|
|
1834
|
+
'pinepaper_font_set_metrics',
|
|
1835
|
+
'pinepaper_font_export',
|
|
1836
|
+
'pinepaper_font_load_into_document',
|
|
1837
|
+
'pinepaper_font_export_data',
|
|
1838
|
+
'pinepaper_font_import_data',
|
|
1839
|
+
'pinepaper_font_clear',
|
|
1840
|
+
'pinepaper_font_remove_overlap',
|
|
1841
|
+
'pinepaper_font_correct_direction',
|
|
1842
|
+
'pinepaper_font_cleanup_path',
|
|
1843
|
+
// Template tools
|
|
1844
|
+
'pinepaper_apply_template',
|
|
1845
|
+
// Image import tools
|
|
1846
|
+
'pinepaper_import_image',
|
|
1847
|
+
// Paper.js direct access tools
|
|
1848
|
+
'pinepaper_register_item',
|
|
951
1849
|
],
|
|
952
1850
|
});
|
|
953
1851
|
}
|