@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
package/dist/tools/handlers.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Supports browser execution via Puppeteer.
|
|
7
7
|
*/
|
|
8
8
|
import { codeGenerator } from '../types/code-generator.js';
|
|
9
|
-
import { CreateItemInputSchema, ModifyItemInputSchema, DeleteItemInputSchema, AddRelationInputSchema, RemoveRelationInputSchema, QueryRelationsInputSchema, AnimateItemInputSchema, KeyframeAnimateInputSchema, ExecuteGeneratorInputSchema, ApplyEffectInputSchema, GetItemsInputSchema, PlayTimelineInputSchema, SetBackgroundColorInputSchema, SetCanvasSizeInputSchema,
|
|
9
|
+
import { CreateItemInputSchema, ModifyItemInputSchema, DeleteItemInputSchema, AddRelationInputSchema, RemoveRelationInputSchema, QueryRelationsInputSchema, AnimateItemInputSchema, KeyframeAnimateInputSchema, ExecuteGeneratorInputSchema, ApplyEffectInputSchema, GetItemsInputSchema, PlayTimelineInputSchema, SetBackgroundColorInputSchema, SetCanvasSizeInputSchema, ImportSVGInputSchema, ImportMermaidInputSchema, AddFilterInputSchema, CreateGlossySphereInputSchema, CreateDiagonalStripesInputSchema, BatchCreateInputSchema, BatchModifyInputSchema, CreateGridInputSchema, CreateSceneInputSchema, GetPerformanceMetricsInputSchema, DiagnosticReportInputSchema, SearchAssetsInputSchema, ImportAssetInputSchema, P5DrawInputSchema,
|
|
10
10
|
// Diagram schemas
|
|
11
11
|
CreateDiagramShapeInputSchema, ConnectInputSchema, ConnectPortsInputSchema, AddPortsInputSchema, AutoLayoutInputSchema, GetDiagramShapesInputSchema, UpdateConnectorInputSchema, RemoveConnectorInputSchema, DiagramModeInputSchema,
|
|
12
12
|
// Agent flow mode schemas
|
|
@@ -17,10 +17,6 @@ AddTriggerInputSchema, RemoveTriggerInputSchema, QueryTriggersInputSchema,
|
|
|
17
17
|
CreateQuizInputSchema, GetQuizStateInputSchema, ResetQuizInputSchema,
|
|
18
18
|
// Letter collage schemas
|
|
19
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
20
|
// Custom relation/code schemas
|
|
25
21
|
RegisterCustomRelationInputSchema, ExecuteCustomCodeInputSchema,
|
|
26
22
|
// Mask schemas
|
|
@@ -29,16 +25,39 @@ ApplyAnimatedMaskInputSchema, ApplyCustomMaskInputSchema, RemoveMaskInputSchema,
|
|
|
29
25
|
ApplyTemplateInputSchema,
|
|
30
26
|
// Image import schemas
|
|
31
27
|
ImportImageInputSchema,
|
|
32
|
-
//
|
|
33
|
-
|
|
28
|
+
// Scene management schemas
|
|
29
|
+
ManageScenesInputSchema, ScenePlaybackInputSchema,
|
|
30
|
+
// New consolidated tool schemas
|
|
31
|
+
SelectionInputSchema, TransformInputSchema, HistoryInputSchema, ImageFilterInputSchema, LassoInputSchema, CutoutStyleInputSchema, PrecompInputSchema, ViewInputSchema, BackgroundInputSchema, QueryInputSchema, DeformInputSchema, SpriteSheetInputSchema, StorageInputSchema, InteractionInputSchema, ExportWidgetInputSchema, ExportWidgetHtmlInputSchema, CreateChartInputSchema, MagicInputSchema, PhysicsInputSchema, MeasurementInputSchema, ErrorCodes, } from '../types/schemas.js';
|
|
34
32
|
import { ZodError } from 'zod';
|
|
35
33
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
36
34
|
import { tmpdir } from 'node:os';
|
|
37
35
|
import { join } from 'node:path';
|
|
38
|
-
import { getBrowserController, } from '../browser/puppeteer-controller.js';
|
|
36
|
+
import { getBrowserController, resetBrowserController, } from '../browser/puppeteer-controller.js';
|
|
37
|
+
import { sameStudioTarget, validateStudioUrl } from '../browser/url-target.js';
|
|
39
38
|
import { getPerformanceTracker } from '../metrics/index.js';
|
|
40
39
|
import { formatErrorContext, captureCanvasState } from '../execution/index.js';
|
|
41
40
|
import { getSessionManager } from '../agent/session-manager.js';
|
|
41
|
+
import { vocabularyHintForPath, validateBatchVocabulary, detectBatchPropertyTypos } from '../ontology/hints.js';
|
|
42
|
+
import { cameraHandlers } from './handlers/camera.js';
|
|
43
|
+
import { fontHandlers } from './handlers/font.js';
|
|
44
|
+
import { toolGuideHandlers } from './handlers/tool-guide.js';
|
|
45
|
+
import { mapHandlers } from './handlers/maps.js';
|
|
46
|
+
import { ontologyHandlers } from './handlers/ontology.js';
|
|
47
|
+
import { exportHandlers } from './handlers/export.js';
|
|
48
|
+
/**
|
|
49
|
+
* Registry of per-domain handler maps. Tools listed here short-circuit the
|
|
50
|
+
* main switch at dispatch time (see handleToolCallInner). New domains are
|
|
51
|
+
* folded in incrementally as they are migrated out of the monolithic switch.
|
|
52
|
+
*/
|
|
53
|
+
const DOMAIN_HANDLERS = [
|
|
54
|
+
cameraHandlers,
|
|
55
|
+
fontHandlers,
|
|
56
|
+
toolGuideHandlers,
|
|
57
|
+
mapHandlers,
|
|
58
|
+
ontologyHandlers,
|
|
59
|
+
exportHandlers,
|
|
60
|
+
];
|
|
42
61
|
/**
|
|
43
62
|
* Get screenshot mode from environment variable.
|
|
44
63
|
* Defaults to 'on_request' for optimal performance.
|
|
@@ -52,43 +71,6 @@ export function getScreenshotMode() {
|
|
|
52
71
|
return 'on_request';
|
|
53
72
|
}
|
|
54
73
|
// =============================================================================
|
|
55
|
-
// SMART DESIGN GUIDANCE — detect if user prompt has specific design direction
|
|
56
|
-
// =============================================================================
|
|
57
|
-
const COLOR_KEYWORDS = /(?:#[0-9a-f]{3,8}|rgb\(|hsl\(|\b(?:red|blue|green|purple|pink|gold|silver|orange|yellow|cyan|magenta|indigo|teal|coral|crimson|navy|maroon|turquoise|violet|amber|emerald|scarlet)\b)/i;
|
|
58
|
-
const STYLE_KEYWORDS = /\b(?:gradient|bokeh|geometric|circuit|wave|sunset|sunrise|minimal|modern|retro|vintage|elegant|playful|abstract|organic|neon|pastel|watercolor|grunge|futuristic|cosmic|dreamy|glossy)\b/i;
|
|
59
|
-
const LAYOUT_KEYWORDS = /\b(?:centered|left-aligned|right-aligned|grid|stacked|scattered|radial|circular|diagonal|symmetrical|horizontal|vertical)\b/i;
|
|
60
|
-
const ANIMATION_KEYWORDS = /\b(?:orbit|follow|pulse|bounce|fade|rotate|wobble|slide|wipe|reveal|parallax|wave|morph|float|glow|sparkle|burst|spiral)\b/i;
|
|
61
|
-
/**
|
|
62
|
-
* Detect if a prompt has enough design specificity to skip guidance injection.
|
|
63
|
-
* Returns true if the prompt is design-specific (skip guidance).
|
|
64
|
-
* Returns false if vague (inject guidance).
|
|
65
|
-
*/
|
|
66
|
-
function detectDesignSpecificity(prompt) {
|
|
67
|
-
if (!prompt || prompt.trim().length === 0)
|
|
68
|
-
return false;
|
|
69
|
-
let specificityScore = 0;
|
|
70
|
-
// Color mentions: strong signal of specificity
|
|
71
|
-
if (COLOR_KEYWORDS.test(prompt))
|
|
72
|
-
specificityScore += 2;
|
|
73
|
-
// Style/mood keywords
|
|
74
|
-
if (STYLE_KEYWORDS.test(prompt))
|
|
75
|
-
specificityScore += 2;
|
|
76
|
-
// Layout direction
|
|
77
|
-
if (LAYOUT_KEYWORDS.test(prompt))
|
|
78
|
-
specificityScore += 1;
|
|
79
|
-
// Animation specifics
|
|
80
|
-
if (ANIMATION_KEYWORDS.test(prompt))
|
|
81
|
-
specificityScore += 1;
|
|
82
|
-
// Word count: detailed prompts tend to be longer
|
|
83
|
-
const wordCount = prompt.trim().split(/\s+/).length;
|
|
84
|
-
if (wordCount >= 20)
|
|
85
|
-
specificityScore += 1;
|
|
86
|
-
if (wordCount >= 35)
|
|
87
|
-
specificityScore += 1;
|
|
88
|
-
// Threshold: 3+ = specific enough, skip guidance
|
|
89
|
-
return specificityScore >= 3;
|
|
90
|
-
}
|
|
91
|
-
// =============================================================================
|
|
92
74
|
// EXPORT FILE SAVE HELPERS
|
|
93
75
|
// =============================================================================
|
|
94
76
|
function getExportDir() {
|
|
@@ -150,18 +132,13 @@ function successResult(code, description) {
|
|
|
150
132
|
return { content };
|
|
151
133
|
}
|
|
152
134
|
function executedResult(code, result, screenshot, description) {
|
|
135
|
+
const resultObj = { success: true, result };
|
|
136
|
+
if (description)
|
|
137
|
+
resultObj.description = description;
|
|
153
138
|
const content = [
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
text: `Executed PinePaper code:\n\n\`\`\`javascript\n${code}\n\`\`\`\n\nResult: ${JSON.stringify(result, null, 2)}`,
|
|
157
|
-
},
|
|
139
|
+
{ type: 'text', text: JSON.stringify(resultObj, null, 2) },
|
|
140
|
+
{ type: 'text', text: `Generated code:\n\`\`\`javascript\n${code}\n\`\`\`` },
|
|
158
141
|
];
|
|
159
|
-
if (description) {
|
|
160
|
-
content.push({
|
|
161
|
-
type: 'text',
|
|
162
|
-
text: `\n${description}`,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
142
|
if (screenshot) {
|
|
166
143
|
content.push({
|
|
167
144
|
type: 'image',
|
|
@@ -182,7 +159,7 @@ function screenshotResult(screenshot) {
|
|
|
182
159
|
],
|
|
183
160
|
};
|
|
184
161
|
}
|
|
185
|
-
function errorResult(code, message, details, context) {
|
|
162
|
+
export function errorResult(code, message, details, context) {
|
|
186
163
|
// Build error object
|
|
187
164
|
const errorObj = {
|
|
188
165
|
success: false,
|
|
@@ -209,10 +186,18 @@ function handleValidationError(error, i18n) {
|
|
|
209
186
|
const message = i18n
|
|
210
187
|
? i18n.getError('validationError', { message: 'Invalid input parameters' })
|
|
211
188
|
: 'Invalid input parameters';
|
|
212
|
-
|
|
213
|
-
path
|
|
214
|
-
message: e.message
|
|
215
|
-
|
|
189
|
+
const details = error.errors.map((e) => {
|
|
190
|
+
const path = e.path.join('.');
|
|
191
|
+
const detail = { path, message: e.message };
|
|
192
|
+
if (e.code === 'invalid_enum_value') {
|
|
193
|
+
const received = e.received;
|
|
194
|
+
const hint = vocabularyHintForPath(path, received);
|
|
195
|
+
if (hint)
|
|
196
|
+
detail.vocabulary = hint;
|
|
197
|
+
}
|
|
198
|
+
return detail;
|
|
199
|
+
});
|
|
200
|
+
return errorResult(ErrorCodes.VALIDATION_ERROR, message, details);
|
|
216
201
|
}
|
|
217
202
|
/**
|
|
218
203
|
* Execute code in browser if connected, otherwise return generated code.
|
|
@@ -228,7 +213,7 @@ function handleValidationError(error, i18n) {
|
|
|
228
213
|
*
|
|
229
214
|
* Default is 'on_request' for optimal performance per best practices.
|
|
230
215
|
*/
|
|
231
|
-
async function executeOrGenerate(code, description, options, toolName) {
|
|
216
|
+
export async function executeOrGenerate(code, description, options, toolName) {
|
|
232
217
|
const { executeInBrowser, browserController, screenshotMode, executionMode } = options;
|
|
233
218
|
const effectiveExecutionMode = executionMode ?? getExecutionMode();
|
|
234
219
|
const effectiveScreenshotMode = screenshotMode ?? getScreenshotMode();
|
|
@@ -405,7 +390,7 @@ ${code}
|
|
|
405
390
|
// =============================================================================
|
|
406
391
|
// I18N HELPER FUNCTIONS
|
|
407
392
|
// =============================================================================
|
|
408
|
-
function getLocalizedSuccessMessage(i18n, key, params) {
|
|
393
|
+
export function getLocalizedSuccessMessage(i18n, key, params) {
|
|
409
394
|
if (!i18n) {
|
|
410
395
|
// Fallback to English template
|
|
411
396
|
return Object.entries(params).reduce((msg, [k, v]) => msg.replace(`{{${k}}}`, String(v)), key);
|
|
@@ -489,6 +474,14 @@ export async function handleToolCall(toolName, args, options = {}) {
|
|
|
489
474
|
async function handleToolCallInner(toolName, args, options, tracker, baseTimerId) {
|
|
490
475
|
const { i18n } = options;
|
|
491
476
|
try {
|
|
477
|
+
// Domain-module dispatch: tools migrated to per-domain handler files
|
|
478
|
+
// short-circuit before the main switch. See DOMAIN_HANDLERS for the
|
|
479
|
+
// current registry.
|
|
480
|
+
for (const domainMap of DOMAIN_HANDLERS) {
|
|
481
|
+
const handler = domainMap[toolName];
|
|
482
|
+
if (handler)
|
|
483
|
+
return await handler(args, options);
|
|
484
|
+
}
|
|
492
485
|
switch (toolName) {
|
|
493
486
|
// -----------------------------------------------------------------------
|
|
494
487
|
// ITEM TOOLS
|
|
@@ -529,7 +522,7 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
529
522
|
const animProps = ['animationType', 'animationSpeed', 'keyframes', 'animation'];
|
|
530
523
|
const passedAnimProps = animProps.filter(p => p in (input.properties || {}));
|
|
531
524
|
if (passedAnimProps.length > 0) {
|
|
532
|
-
return errorResult(
|
|
525
|
+
return errorResult(ErrorCodes.INVALID_PROPERTIES, `Animation properties (${passedAnimProps.join(', ')}) cannot be set via pinepaper_modify_item — they will be silently ignored. ` +
|
|
533
526
|
`Use pinepaper_animate for loop animations (pulse, rotate, bounce, fade, wobble, slide) ` +
|
|
534
527
|
`or pinepaper_keyframe_animate for timed/sequenced animations with precise control.`);
|
|
535
528
|
}
|
|
@@ -571,6 +564,134 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
571
564
|
const description = `Creates complete scene with ${itemCount} items, ${relationCount} relations, ${animationCount} animations`;
|
|
572
565
|
return executeOrGenerate(code, description, options, 'pinepaper_create_scene');
|
|
573
566
|
}
|
|
567
|
+
case 'pinepaper_manage_scenes': {
|
|
568
|
+
const input = ManageScenesInputSchema.parse(args);
|
|
569
|
+
const code = codeGenerator.generateManageScenes(input);
|
|
570
|
+
return executeOrGenerate(code, `Scene: ${input.action}`, options, 'pinepaper_manage_scenes');
|
|
571
|
+
}
|
|
572
|
+
case 'pinepaper_scene_playback': {
|
|
573
|
+
const input = ScenePlaybackInputSchema.parse(args);
|
|
574
|
+
const code = codeGenerator.generateScenePlayback(input);
|
|
575
|
+
return executeOrGenerate(code, `Scene playback: ${input.action}`, options, 'pinepaper_scene_playback');
|
|
576
|
+
}
|
|
577
|
+
// -----------------------------------------------------------------------
|
|
578
|
+
// SELECTION, TRANSFORM & HISTORY TOOLS
|
|
579
|
+
// -----------------------------------------------------------------------
|
|
580
|
+
case 'pinepaper_selection': {
|
|
581
|
+
const input = SelectionInputSchema.parse(args);
|
|
582
|
+
const code = codeGenerator.generateSelection(input);
|
|
583
|
+
return executeOrGenerate(code, `Selection: ${input.action}`, options, 'pinepaper_selection');
|
|
584
|
+
}
|
|
585
|
+
case 'pinepaper_transform': {
|
|
586
|
+
const input = TransformInputSchema.parse(args);
|
|
587
|
+
const code = codeGenerator.generateTransform(input);
|
|
588
|
+
return executeOrGenerate(code, `Transform: ${input.action}`, options, 'pinepaper_transform');
|
|
589
|
+
}
|
|
590
|
+
case 'pinepaper_history': {
|
|
591
|
+
const input = HistoryInputSchema.parse(args);
|
|
592
|
+
const code = codeGenerator.generateHistory(input);
|
|
593
|
+
return executeOrGenerate(code, `History: ${input.action}`, options, 'pinepaper_history');
|
|
594
|
+
}
|
|
595
|
+
// -----------------------------------------------------------------------
|
|
596
|
+
// IMAGE PROCESSING TOOLS
|
|
597
|
+
// -----------------------------------------------------------------------
|
|
598
|
+
case 'pinepaper_image_filter': {
|
|
599
|
+
const input = ImageFilterInputSchema.parse(args);
|
|
600
|
+
const code = codeGenerator.generateImageFilter(input);
|
|
601
|
+
return executeOrGenerate(code, `Image filter: ${input.action}`, options, 'pinepaper_image_filter');
|
|
602
|
+
}
|
|
603
|
+
case 'pinepaper_lasso': {
|
|
604
|
+
const input = LassoInputSchema.parse(args);
|
|
605
|
+
const code = codeGenerator.generateLasso(input);
|
|
606
|
+
return executeOrGenerate(code, `Lasso: ${input.action}`, options, 'pinepaper_lasso');
|
|
607
|
+
}
|
|
608
|
+
case 'pinepaper_cutout_style': {
|
|
609
|
+
const input = CutoutStyleInputSchema.parse(args);
|
|
610
|
+
const code = codeGenerator.generateCutoutStyle(input);
|
|
611
|
+
return executeOrGenerate(code, `Cutout style: ${input.action}`, options, 'pinepaper_cutout_style');
|
|
612
|
+
}
|
|
613
|
+
// -----------------------------------------------------------------------
|
|
614
|
+
// COMPOSITION & VIEW TOOLS
|
|
615
|
+
// -----------------------------------------------------------------------
|
|
616
|
+
case 'pinepaper_precomp': {
|
|
617
|
+
const input = PrecompInputSchema.parse(args);
|
|
618
|
+
const code = codeGenerator.generatePrecomp(input);
|
|
619
|
+
return executeOrGenerate(code, `Precomp: ${input.action}`, options, 'pinepaper_precomp');
|
|
620
|
+
}
|
|
621
|
+
case 'pinepaper_view': {
|
|
622
|
+
const input = ViewInputSchema.parse(args);
|
|
623
|
+
const code = codeGenerator.generateView(input);
|
|
624
|
+
return executeOrGenerate(code, `View: ${input.action}`, options, 'pinepaper_view');
|
|
625
|
+
}
|
|
626
|
+
case 'pinepaper_background': {
|
|
627
|
+
const input = BackgroundInputSchema.parse(args);
|
|
628
|
+
const code = codeGenerator.generateBackground(input);
|
|
629
|
+
return executeOrGenerate(code, `Background: ${input.action}`, options, 'pinepaper_background');
|
|
630
|
+
}
|
|
631
|
+
// -----------------------------------------------------------------------
|
|
632
|
+
// CANVAS QUERY TOOLS
|
|
633
|
+
// -----------------------------------------------------------------------
|
|
634
|
+
case 'pinepaper_query': {
|
|
635
|
+
const input = QueryInputSchema.parse(args);
|
|
636
|
+
const code = codeGenerator.generateQuery(input);
|
|
637
|
+
return executeOrGenerate(code, `Query: ${input.action}`, options, 'pinepaper_query');
|
|
638
|
+
}
|
|
639
|
+
// -----------------------------------------------------------------------
|
|
640
|
+
// DEFORMATION, SPRITE SHEETS, STORAGE, INTERACTION
|
|
641
|
+
// -----------------------------------------------------------------------
|
|
642
|
+
case 'pinepaper_deform': {
|
|
643
|
+
const input = DeformInputSchema.parse(args);
|
|
644
|
+
const code = codeGenerator.generateDeform(input);
|
|
645
|
+
return executeOrGenerate(code, `Deform: ${input.action}`, options, 'pinepaper_deform');
|
|
646
|
+
}
|
|
647
|
+
case 'pinepaper_sprite_sheet': {
|
|
648
|
+
const input = SpriteSheetInputSchema.parse(args);
|
|
649
|
+
const code = codeGenerator.generateSpriteSheet(input);
|
|
650
|
+
return executeOrGenerate(code, `Sprite sheet: ${input.action}`, options, 'pinepaper_sprite_sheet');
|
|
651
|
+
}
|
|
652
|
+
case 'pinepaper_storage': {
|
|
653
|
+
const input = StorageInputSchema.parse(args);
|
|
654
|
+
const code = codeGenerator.generateStorage(input);
|
|
655
|
+
return executeOrGenerate(code, `Storage: ${input.action}`, options, 'pinepaper_storage');
|
|
656
|
+
}
|
|
657
|
+
case 'pinepaper_interaction': {
|
|
658
|
+
const input = InteractionInputSchema.parse(args);
|
|
659
|
+
const code = codeGenerator.generateInteraction(input);
|
|
660
|
+
return executeOrGenerate(code, `Interaction: ${input.action}`, options, 'pinepaper_interaction');
|
|
661
|
+
}
|
|
662
|
+
case 'pinepaper_export_widget': {
|
|
663
|
+
const input = ExportWidgetInputSchema.parse(args);
|
|
664
|
+
const code = codeGenerator.generateExportWidget(input);
|
|
665
|
+
return executeOrGenerate(code, 'Export widget JSON', options, 'pinepaper_export_widget');
|
|
666
|
+
}
|
|
667
|
+
case 'pinepaper_export_widget_html': {
|
|
668
|
+
const input = ExportWidgetHtmlInputSchema.parse(args);
|
|
669
|
+
const code = codeGenerator.generateExportWidgetHtml(input);
|
|
670
|
+
return executeOrGenerate(code, 'Export widget HTML', options, 'pinepaper_export_widget_html');
|
|
671
|
+
}
|
|
672
|
+
// -----------------------------------------------------------------------
|
|
673
|
+
// DATA VISUALIZATION, MAGIC, PHYSICS, MEASUREMENT
|
|
674
|
+
// -----------------------------------------------------------------------
|
|
675
|
+
case 'pinepaper_create_chart': {
|
|
676
|
+
const input = CreateChartInputSchema.parse(args);
|
|
677
|
+
const code = codeGenerator.generateChart(input);
|
|
678
|
+
return executeOrGenerate(code, `Chart: ${input.action}`, options, 'pinepaper_create_chart');
|
|
679
|
+
}
|
|
680
|
+
case 'pinepaper_magic': {
|
|
681
|
+
const input = MagicInputSchema.parse(args);
|
|
682
|
+
const code = codeGenerator.generateMagic(input);
|
|
683
|
+
return executeOrGenerate(code, `Magic: ${input.action}`, options, 'pinepaper_magic');
|
|
684
|
+
}
|
|
685
|
+
case 'pinepaper_physics': {
|
|
686
|
+
const input = PhysicsInputSchema.parse(args);
|
|
687
|
+
const code = codeGenerator.generatePhysics(input);
|
|
688
|
+
return executeOrGenerate(code, `Physics: ${input.action}`, options, 'pinepaper_physics');
|
|
689
|
+
}
|
|
690
|
+
case 'pinepaper_measurement': {
|
|
691
|
+
const input = MeasurementInputSchema.parse(args);
|
|
692
|
+
const code = codeGenerator.generateMeasurement(input);
|
|
693
|
+
return executeOrGenerate(code, `Measurement: ${input.action}`, options, 'pinepaper_measurement');
|
|
694
|
+
}
|
|
574
695
|
// -----------------------------------------------------------------------
|
|
575
696
|
// BATCH OPERATION TOOLS
|
|
576
697
|
// -----------------------------------------------------------------------
|
|
@@ -714,168 +835,11 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
714
835
|
return executeOrGenerate(code, 'Gets available mask animation presets', options, 'pinepaper_get_mask_animations');
|
|
715
836
|
}
|
|
716
837
|
// -----------------------------------------------------------------------
|
|
717
|
-
// CAMERA TOOLS
|
|
718
|
-
// -----------------------------------------------------------------------
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
const loop = args.loop ?? false;
|
|
723
|
-
const delay = args.delay ?? 0;
|
|
724
|
-
const keyframesStr = JSON.stringify(keyframes);
|
|
725
|
-
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} });`;
|
|
726
|
-
return executeOrGenerate(code, `Animates camera with ${keyframes.length} keyframes over ${duration}s`, options, 'pinepaper_camera_animate');
|
|
727
|
-
}
|
|
728
|
-
case 'pinepaper_camera_zoom': {
|
|
729
|
-
const direction = args.direction;
|
|
730
|
-
const level = args.level ?? (direction === 'in' ? 2 : 0.5);
|
|
731
|
-
const duration = args.duration ?? 0.5;
|
|
732
|
-
const method = direction === 'in' ? 'zoomIn' : 'zoomOut';
|
|
733
|
-
const code = `app.camera && app.camera.${method} ? app.camera.${method}(${level}, ${duration}) : null;`;
|
|
734
|
-
return executeOrGenerate(code, `Camera zoom ${direction} to ${level}x`, options, 'pinepaper_camera_zoom');
|
|
735
|
-
}
|
|
736
|
-
case 'pinepaper_camera_pan': {
|
|
737
|
-
const direction = args.direction;
|
|
738
|
-
const amount = args.amount ?? 100;
|
|
739
|
-
const x = args.x;
|
|
740
|
-
const y = args.y;
|
|
741
|
-
const duration = args.duration ?? 0.5;
|
|
742
|
-
let code;
|
|
743
|
-
let description;
|
|
744
|
-
if (x !== undefined && y !== undefined) {
|
|
745
|
-
code = `app.camera && app.camera.panTo ? app.camera.panTo(${x}, ${y}, ${duration}) : null;`;
|
|
746
|
-
description = `Camera pan to (${x}, ${y})`;
|
|
747
|
-
}
|
|
748
|
-
else if (direction) {
|
|
749
|
-
const methodMap = { left: 'panLeft', right: 'panRight', up: 'panUp', down: 'panDown' };
|
|
750
|
-
const method = methodMap[direction];
|
|
751
|
-
code = `app.camera && app.camera.${method} ? app.camera.${method}(${amount}, ${duration}) : null;`;
|
|
752
|
-
description = `Camera pan ${direction} by ${amount}px`;
|
|
753
|
-
}
|
|
754
|
-
else {
|
|
755
|
-
code = `// No direction or coordinates specified`;
|
|
756
|
-
description = 'Camera pan (no parameters)';
|
|
757
|
-
}
|
|
758
|
-
return executeOrGenerate(code, description, options, 'pinepaper_camera_pan');
|
|
759
|
-
}
|
|
760
|
-
case 'pinepaper_camera_move_to': {
|
|
761
|
-
const x = args.x;
|
|
762
|
-
const y = args.y;
|
|
763
|
-
const zoom = args.zoom;
|
|
764
|
-
const duration = args.duration ?? 0.5;
|
|
765
|
-
const code = `app.camera && app.camera.moveTo ? app.camera.moveTo(${x}, ${y}, ${zoom}, ${duration}) : null;`;
|
|
766
|
-
return executeOrGenerate(code, `Camera move to (${x}, ${y}) at ${zoom}x zoom`, options, 'pinepaper_camera_move_to');
|
|
767
|
-
}
|
|
768
|
-
case 'pinepaper_camera_reset': {
|
|
769
|
-
const duration = args.duration ?? 0.5;
|
|
770
|
-
const code = `app.camera && app.camera.reset ? app.camera.reset(${duration}) : null;`;
|
|
771
|
-
return executeOrGenerate(code, 'Reset camera to default state', options, 'pinepaper_camera_reset');
|
|
772
|
-
}
|
|
773
|
-
case 'pinepaper_camera_stop': {
|
|
774
|
-
const code = `app.camera && app.camera.stop ? app.camera.stop() : null;`;
|
|
775
|
-
return executeOrGenerate(code, 'Stop camera animation', options, 'pinepaper_camera_stop');
|
|
776
|
-
}
|
|
777
|
-
case 'pinepaper_camera_state': {
|
|
778
|
-
const code = `app.camera && app.camera.getState ? app.camera.getState() : { zoom: 1, center: [400, 300], isAnimating: false };`;
|
|
779
|
-
return executeOrGenerate(code, 'Get current camera state', options, 'pinepaper_camera_state');
|
|
780
|
-
}
|
|
781
|
-
// -----------------------------------------------------------------------
|
|
782
|
-
// FONT TOOLS
|
|
783
|
-
// -----------------------------------------------------------------------
|
|
784
|
-
case 'pinepaper_font_show_studio': {
|
|
785
|
-
const code = `app.fontStudio && app.fontStudio.show ? app.fontStudio.show() : null;`;
|
|
786
|
-
return executeOrGenerate(code, 'Opens Font Studio UI', options, 'pinepaper_font_show_studio');
|
|
787
|
-
}
|
|
788
|
-
case 'pinepaper_font_set_name': {
|
|
789
|
-
const { name } = args;
|
|
790
|
-
const code = `app.fontStudio.setName(${JSON.stringify(name)});`;
|
|
791
|
-
return executeOrGenerate(code, `Set font name to "${name}"`, options, 'pinepaper_font_set_name');
|
|
792
|
-
}
|
|
793
|
-
case 'pinepaper_font_get_required_chars': {
|
|
794
|
-
const { set } = args;
|
|
795
|
-
const setArg = set ? JSON.stringify(set) : '"minimum"';
|
|
796
|
-
const code = `app.fontStudio.getRequiredChars(${setArg});`;
|
|
797
|
-
return executeOrGenerate(code, `Get required characters (${set || 'minimum'} set)`, options, 'pinepaper_font_get_required_chars');
|
|
798
|
-
}
|
|
799
|
-
case 'pinepaper_font_get_status': {
|
|
800
|
-
const code = `app.fontStudio.getStatus();`;
|
|
801
|
-
return executeOrGenerate(code, 'Get font completion status', options, 'pinepaper_font_get_status');
|
|
802
|
-
}
|
|
803
|
-
case 'pinepaper_font_create_glyph': {
|
|
804
|
-
const { character, pathId } = args;
|
|
805
|
-
const code = `app.fontStudio.createGlyph(${JSON.stringify(character)}, ${JSON.stringify(pathId)});`;
|
|
806
|
-
return executeOrGenerate(code, `Create glyph for "${character}" from path ${pathId}`, options, 'pinepaper_font_create_glyph');
|
|
807
|
-
}
|
|
808
|
-
case 'pinepaper_font_create_space': {
|
|
809
|
-
const { width } = args;
|
|
810
|
-
const code = width !== undefined
|
|
811
|
-
? `app.fontStudio.createSpace(${width});`
|
|
812
|
-
: `app.fontStudio.createSpace();`;
|
|
813
|
-
return executeOrGenerate(code, `Create space glyph${width ? ` (width: ${width})` : ''}`, options, 'pinepaper_font_create_space');
|
|
814
|
-
}
|
|
815
|
-
case 'pinepaper_font_remove_glyph': {
|
|
816
|
-
const { character } = args;
|
|
817
|
-
const code = `app.fontStudio.removeGlyph(${JSON.stringify(character)});`;
|
|
818
|
-
return executeOrGenerate(code, `Remove glyph for "${character}"`, options, 'pinepaper_font_remove_glyph');
|
|
819
|
-
}
|
|
820
|
-
case 'pinepaper_font_set_metrics': {
|
|
821
|
-
const metrics = args;
|
|
822
|
-
const code = `app.fontStudio.setMetrics(${JSON.stringify(metrics)});`;
|
|
823
|
-
return executeOrGenerate(code, 'Set font metrics', options, 'pinepaper_font_set_metrics');
|
|
824
|
-
}
|
|
825
|
-
case 'pinepaper_font_export': {
|
|
826
|
-
const { download } = args;
|
|
827
|
-
const code = download === false
|
|
828
|
-
? `app.fontStudio.export({ download: false });`
|
|
829
|
-
: `app.fontStudio.export();`;
|
|
830
|
-
return executeOrGenerate(code, 'Export font as OTF', options, 'pinepaper_font_export');
|
|
831
|
-
}
|
|
832
|
-
case 'pinepaper_font_load_into_document': {
|
|
833
|
-
const code = `app.fontStudio.loadIntoDocument();`;
|
|
834
|
-
return executeOrGenerate(code, 'Load font into document', options, 'pinepaper_font_load_into_document');
|
|
835
|
-
}
|
|
836
|
-
case 'pinepaper_font_export_data': {
|
|
837
|
-
const { download } = args;
|
|
838
|
-
const code = download === false
|
|
839
|
-
? `app.fontStudio.exportData({ download: false });`
|
|
840
|
-
: `app.fontStudio.exportData();`;
|
|
841
|
-
return executeOrGenerate(code, 'Export font data as JSON', options, 'pinepaper_font_export_data');
|
|
842
|
-
}
|
|
843
|
-
case 'pinepaper_font_import_data': {
|
|
844
|
-
const { data } = args;
|
|
845
|
-
const code = `app.fontStudio.importData(${JSON.stringify(data)});`;
|
|
846
|
-
return executeOrGenerate(code, 'Import font data from JSON', options, 'pinepaper_font_import_data');
|
|
847
|
-
}
|
|
848
|
-
case 'pinepaper_font_clear': {
|
|
849
|
-
const code = `app.fontStudio.clear();`;
|
|
850
|
-
return executeOrGenerate(code, 'Clear all glyphs and reset font', options, 'pinepaper_font_clear');
|
|
851
|
-
}
|
|
852
|
-
case 'pinepaper_font_remove_overlap': {
|
|
853
|
-
const { pathId } = args;
|
|
854
|
-
const code = `app.fontStudio.removeOverlap(${JSON.stringify(pathId)});`;
|
|
855
|
-
return executeOrGenerate(code, `Remove overlaps from path ${pathId}`, options, 'pinepaper_font_remove_overlap');
|
|
856
|
-
}
|
|
857
|
-
case 'pinepaper_font_correct_direction': {
|
|
858
|
-
const { pathId } = args;
|
|
859
|
-
const code = `app.fontStudio.correctDirection(${JSON.stringify(pathId)});`;
|
|
860
|
-
return executeOrGenerate(code, `Correct path direction for ${pathId}`, options, 'pinepaper_font_correct_direction');
|
|
861
|
-
}
|
|
862
|
-
case 'pinepaper_font_cleanup_path': {
|
|
863
|
-
const { pathId, removeOverlap, correctDirection, smooth, smoothTolerance } = args;
|
|
864
|
-
const optsObj = {};
|
|
865
|
-
if (removeOverlap !== undefined)
|
|
866
|
-
optsObj.removeOverlap = removeOverlap;
|
|
867
|
-
if (correctDirection !== undefined)
|
|
868
|
-
optsObj.correctDirection = correctDirection;
|
|
869
|
-
if (smooth !== undefined)
|
|
870
|
-
optsObj.smooth = smooth;
|
|
871
|
-
if (smoothTolerance !== undefined)
|
|
872
|
-
optsObj.smoothTolerance = smoothTolerance;
|
|
873
|
-
const hasOpts = Object.keys(optsObj).length > 0;
|
|
874
|
-
const code = hasOpts
|
|
875
|
-
? `app.fontStudio.cleanupPath(${JSON.stringify(pathId)}, ${JSON.stringify(optsObj)});`
|
|
876
|
-
: `app.fontStudio.cleanupPath(${JSON.stringify(pathId)});`;
|
|
877
|
-
return executeOrGenerate(code, `Cleanup path ${pathId}`, options, 'pinepaper_font_cleanup_path');
|
|
878
|
-
}
|
|
838
|
+
// CAMERA TOOLS — extracted to src/tools/handlers/camera.ts
|
|
839
|
+
// -----------------------------------------------------------------------
|
|
840
|
+
// -----------------------------------------------------------------------
|
|
841
|
+
// FONT TOOLS — extracted to src/tools/handlers/font.ts
|
|
842
|
+
// -----------------------------------------------------------------------
|
|
879
843
|
// -----------------------------------------------------------------------
|
|
880
844
|
// QUERY TOOLS
|
|
881
845
|
// -----------------------------------------------------------------------
|
|
@@ -920,7 +884,7 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
920
884
|
case 'pinepaper_refresh_page': {
|
|
921
885
|
const controller = options.browserController || getBrowserController();
|
|
922
886
|
if (!controller.connected) {
|
|
923
|
-
return errorResult(
|
|
887
|
+
return errorResult(ErrorCodes.BROWSER_NOT_CONNECTED, 'Not connected to PinePaper Studio. Call pinepaper_browser_connect first.');
|
|
924
888
|
}
|
|
925
889
|
try {
|
|
926
890
|
// Refresh the page
|
|
@@ -949,7 +913,7 @@ You can now start creating new items on a clean canvas.`,
|
|
|
949
913
|
return { content };
|
|
950
914
|
}
|
|
951
915
|
catch (error) {
|
|
952
|
-
return errorResult(
|
|
916
|
+
return errorResult(ErrorCodes.REFRESH_FAILED, error instanceof Error ? error.message : 'Failed to refresh page');
|
|
953
917
|
}
|
|
954
918
|
}
|
|
955
919
|
// -----------------------------------------------------------------------
|
|
@@ -961,6 +925,15 @@ You can now start creating new items on a clean canvas.`,
|
|
|
961
925
|
const description = 'Imports SVG onto the canvas';
|
|
962
926
|
return executeOrGenerate(code, description, options, 'pinepaper_import_svg');
|
|
963
927
|
}
|
|
928
|
+
case 'pinepaper_import_mermaid': {
|
|
929
|
+
const input = ImportMermaidInputSchema.parse(args);
|
|
930
|
+
const code = codeGenerator.generateImportMermaid(input.mermaidText, {
|
|
931
|
+
autoLayout: input.autoLayout,
|
|
932
|
+
clearExisting: input.clearExisting,
|
|
933
|
+
});
|
|
934
|
+
const description = 'Imports a Mermaid diagram onto the canvas';
|
|
935
|
+
return executeOrGenerate(code, description, options, 'pinepaper_import_mermaid');
|
|
936
|
+
}
|
|
964
937
|
// -----------------------------------------------------------------------
|
|
965
938
|
// FILTER TOOLS
|
|
966
939
|
// -----------------------------------------------------------------------
|
|
@@ -971,111 +944,57 @@ You can now start creating new items on a clean canvas.`,
|
|
|
971
944
|
return executeOrGenerate(code, description, options, 'pinepaper_add_filter');
|
|
972
945
|
}
|
|
973
946
|
// -----------------------------------------------------------------------
|
|
974
|
-
// EXPORT TOOLS
|
|
947
|
+
// EXPORT TOOLS — extracted to src/tools/handlers/export.ts
|
|
975
948
|
// -----------------------------------------------------------------------
|
|
976
|
-
case 'pinepaper_export_svg': {
|
|
977
|
-
const code = codeGenerator.generateExportSVG();
|
|
978
|
-
const description = getLocalizedSuccessMessage(i18n, 'exported', {
|
|
979
|
-
format: 'SVG',
|
|
980
|
-
});
|
|
981
|
-
return executeOrGenerate(code, description, options, 'pinepaper_export_svg');
|
|
982
|
-
}
|
|
983
|
-
case 'pinepaper_export_training_data': {
|
|
984
|
-
const input = ExportTrainingDataInputSchema.parse(args);
|
|
985
|
-
const code = codeGenerator.generateExportTrainingData(input.format, input.includeMetadata);
|
|
986
|
-
const description = getLocalizedSuccessMessage(i18n, 'exported', {
|
|
987
|
-
format: input.format?.toUpperCase() || 'JSON',
|
|
988
|
-
});
|
|
989
|
-
return executeOrGenerate(code, description, options, 'pinepaper_export_training_data');
|
|
990
|
-
}
|
|
991
|
-
case 'pinepaper_export_scene': {
|
|
992
|
-
const code = codeGenerator.generateExportScene();
|
|
993
|
-
return executeOrGenerate(code, 'Exports complete scene state', options, 'pinepaper_export_scene');
|
|
994
|
-
}
|
|
995
949
|
// -----------------------------------------------------------------------
|
|
996
950
|
// BROWSER CONTROL TOOLS
|
|
997
951
|
// -----------------------------------------------------------------------
|
|
998
952
|
case 'pinepaper_browser_connect': {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
953
|
+
// Default to headless: false — if you explicitly call browser_connect, you likely want to see it
|
|
954
|
+
const headless = args.headless ?? false;
|
|
955
|
+
// Target a custom Studio instance (e.g. local dev at http://localhost:3000).
|
|
956
|
+
// Falls back to the PINEPAPER_STUDIO_URL env var, else PuppeteerController's
|
|
957
|
+
// built-in prod default. "/editor" + agent params are appended by the controller.
|
|
958
|
+
const rawUrl = typeof args.url === 'string' ? args.url.trim() : '';
|
|
959
|
+
const studioUrl = (rawUrl || (process.env.PINEPAPER_STUDIO_URL || '').trim()) || undefined;
|
|
960
|
+
// Only http(s) is supported — reject file://, data://, javascript:, etc.
|
|
961
|
+
if (studioUrl) {
|
|
962
|
+
const v = validateStudioUrl(studioUrl);
|
|
963
|
+
if (!v.ok)
|
|
964
|
+
return errorResult(ErrorCodes.INVALID_INPUT, v.error);
|
|
965
|
+
}
|
|
966
|
+
const cfg = studioUrl ? { headless, studioUrl } : { headless };
|
|
967
|
+
const controller = getBrowserController(cfg);
|
|
968
|
+
const urlChanged = !!studioUrl && controller.connected &&
|
|
969
|
+
!sameStudioTarget(controller.studioUrl, studioUrl);
|
|
970
|
+
// Already connected, same headless mode, same URL → nothing to do
|
|
971
|
+
if (controller.connected && controller.isHeadless === headless && !urlChanged) {
|
|
1004
972
|
return {
|
|
1005
|
-
content: [
|
|
1006
|
-
{
|
|
1007
|
-
type: 'text',
|
|
1008
|
-
text: `Already connected to PinePaper Studio at ${controller.actualUrl}. Do NOT call this tool again — proceed directly with pinepaper_agent_start_job.`,
|
|
1009
|
-
},
|
|
1010
|
-
],
|
|
973
|
+
content: [{ type: 'text', text: `Already connected to PinePaper Studio (${controller.studioUrl || 'default'}, headless: ${headless}).` }],
|
|
1011
974
|
};
|
|
1012
975
|
}
|
|
976
|
+
// Connected but headless mode OR target URL changed → tear down so the
|
|
977
|
+
// new config takes effect (getBrowserController ignores config once the
|
|
978
|
+
// singleton exists).
|
|
979
|
+
if (controller.connected && (controller.isHeadless !== headless || urlChanged)) {
|
|
980
|
+
await resetBrowserController();
|
|
981
|
+
}
|
|
1013
982
|
try {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
983
|
+
const newController = controller.connected ? controller : getBrowserController(cfg);
|
|
984
|
+
if (!newController.connected)
|
|
985
|
+
await newController.connect();
|
|
986
|
+
const canvasSizeResult = await newController.executeCode(`const size = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 }; ({ width: size.width || 800, height: size.height || 600 });`, false);
|
|
1017
987
|
const canvasSize = canvasSizeResult.success
|
|
1018
988
|
? canvasSizeResult.result
|
|
1019
989
|
: { width: 800, height: 600 };
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
type: 'text',
|
|
1024
|
-
text: `✅ Connected to PinePaper Studio at ${controller.actualUrl}
|
|
1025
|
-
|
|
1026
|
-
**Agent mode enabled** (agent=1&mode=agent parameters added automatically)
|
|
1027
|
-
**Current canvas size: ${canvasSize.width}x${canvasSize.height}**
|
|
1028
|
-
|
|
1029
|
-
⚠️ IMPORTANT NOTES:
|
|
1030
|
-
1. **Welcome Template**: First-time visitors see a welcome template with example items. Use \`pinepaper_clear_canvas\` or \`pinepaper_refresh_page\` to start with an empty canvas.
|
|
1031
|
-
2. **Canvas Size**: For complex designs (cards, invitations, detailed graphics), call \`pinepaper_set_canvas_size\` FIRST to set an appropriate size like 1080x1080 or larger. The default 800x600 may be too small.
|
|
1032
|
-
3. **Verify with Screenshots**: After making changes, use \`pinepaper_browser_screenshot\` to confirm your work.
|
|
1033
|
-
|
|
1034
|
-
Browser is ready. You can now use other pinepaper tools to create and animate graphics.`,
|
|
1035
|
-
},
|
|
1036
|
-
];
|
|
1037
|
-
if (screenshot) {
|
|
1038
|
-
content.push({
|
|
1039
|
-
type: 'image',
|
|
1040
|
-
data: screenshot,
|
|
1041
|
-
mimeType: 'image/png',
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1044
|
-
return { content };
|
|
990
|
+
return {
|
|
991
|
+
content: [{ type: 'text', text: `Connected to PinePaper Studio (${newController.studioUrl || 'default'}, headless: ${headless}, canvas: ${canvasSize.width}x${canvasSize.height}).` }],
|
|
992
|
+
};
|
|
1045
993
|
}
|
|
1046
994
|
catch (error) {
|
|
1047
|
-
const errorMessage = error instanceof Error ? error.message : 'Failed to connect
|
|
1048
|
-
// Provide helpful fallback instructions instead of just failing
|
|
995
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to connect';
|
|
1049
996
|
return {
|
|
1050
|
-
content: [
|
|
1051
|
-
{
|
|
1052
|
-
type: 'text',
|
|
1053
|
-
text: `⚠️ **Browser Connection Failed**
|
|
1054
|
-
|
|
1055
|
-
**Error:** ${errorMessage}
|
|
1056
|
-
|
|
1057
|
-
## Don't worry! You can still use PinePaper manually:
|
|
1058
|
-
|
|
1059
|
-
### Option 1: Manual Browser Mode
|
|
1060
|
-
1. Open **PinePaper Studio** in your browser: https://pinepaper.studio/editor?agent=1&mode=agent
|
|
1061
|
-
2. Continue using PinePaper tools - the MCP server will generate code for each action
|
|
1062
|
-
3. Copy the generated code and paste it into the **Code Console** (</> icon in toolbar)
|
|
1063
|
-
|
|
1064
|
-
### Option 2: Troubleshoot Browser Connection
|
|
1065
|
-
- **Check Chrome/Chromium**: Puppeteer requires Chrome or Chromium installed
|
|
1066
|
-
- **Try headless: false**: Call \`pinepaper_browser_connect\` with \`headless: false\` to see the browser window
|
|
1067
|
-
- **Check permissions**: Ensure the process has permission to launch browsers
|
|
1068
|
-
- **Firewall**: Check if firewall is blocking browser launch
|
|
1069
|
-
|
|
1070
|
-
### What happens now?
|
|
1071
|
-
All PinePaper tools will work in **code-only mode**:
|
|
1072
|
-
- Each tool generates JavaScript code
|
|
1073
|
-
- You copy the code and paste it in PinePaper's Code Console
|
|
1074
|
-
- The code executes and creates your graphics
|
|
1075
|
-
|
|
1076
|
-
**The MCP server is fully functional** - just without automatic browser control.`,
|
|
1077
|
-
},
|
|
1078
|
-
],
|
|
997
|
+
content: [{ type: 'text', text: `Browser connection failed: ${errorMessage}` }],
|
|
1079
998
|
};
|
|
1080
999
|
}
|
|
1081
1000
|
}
|
|
@@ -1104,13 +1023,13 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1104
1023
|
case 'pinepaper_browser_screenshot': {
|
|
1105
1024
|
const controller = getBrowserController();
|
|
1106
1025
|
if (!controller.connected) {
|
|
1107
|
-
return errorResult(
|
|
1026
|
+
return errorResult(ErrorCodes.BROWSER_NOT_CONNECTED, 'Not connected to PinePaper Studio. Call pinepaper_browser_connect first.');
|
|
1108
1027
|
}
|
|
1109
1028
|
const screenshot = await controller.takeScreenshot();
|
|
1110
1029
|
if (!screenshot) {
|
|
1111
1030
|
// Capture canvas state for debugging
|
|
1112
1031
|
const canvasState = await captureCanvasState(controller);
|
|
1113
|
-
return errorResult(
|
|
1032
|
+
return errorResult(ErrorCodes.SCREENSHOT_FAILED, 'Failed to capture screenshot', undefined, {
|
|
1114
1033
|
toolName: 'pinepaper_browser_screenshot',
|
|
1115
1034
|
canvasState: canvasState || undefined,
|
|
1116
1035
|
});
|
|
@@ -1397,54 +1316,8 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1397
1316
|
};
|
|
1398
1317
|
}
|
|
1399
1318
|
// -----------------------------------------------------------------------
|
|
1400
|
-
// TOOL GUIDE
|
|
1319
|
+
// TOOL GUIDE — extracted to src/tools/handlers/tool-guide.ts
|
|
1401
1320
|
// -----------------------------------------------------------------------
|
|
1402
|
-
case 'pinepaper_tool_guide': {
|
|
1403
|
-
const input = ToolGuideInputSchema.parse(args);
|
|
1404
|
-
const { PINEPAPER_TOOLS, AI_AGENT_GUIDE } = await import('./definitions.js');
|
|
1405
|
-
const { TOOL_TAGS } = await import('./toolkits.js');
|
|
1406
|
-
// No args → full AI Agent Guide
|
|
1407
|
-
if (!input.tool && !input.category) {
|
|
1408
|
-
return {
|
|
1409
|
-
content: [{ type: 'text', text: AI_AGENT_GUIDE }],
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1412
|
-
// Specific tool → full verbose description
|
|
1413
|
-
if (input.tool) {
|
|
1414
|
-
const tool = PINEPAPER_TOOLS.find((t) => t.name === input.tool);
|
|
1415
|
-
if (!tool) {
|
|
1416
|
-
return {
|
|
1417
|
-
content: [{ type: 'text', text: `Unknown tool: ${input.tool}. Use pinepaper_tool_guide with no args to see the AI Agent Guide.` }],
|
|
1418
|
-
isError: true,
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
return {
|
|
1422
|
-
content: [{ type: 'text', text: `# ${tool.name}\n\n${tool.description}` }],
|
|
1423
|
-
};
|
|
1424
|
-
}
|
|
1425
|
-
// Category → all tools in that tag with descriptions
|
|
1426
|
-
if (input.category) {
|
|
1427
|
-
const toolNames = TOOL_TAGS[input.category];
|
|
1428
|
-
if (!toolNames) {
|
|
1429
|
-
const available = Object.keys(TOOL_TAGS).join(', ');
|
|
1430
|
-
return {
|
|
1431
|
-
content: [{ type: 'text', text: `Unknown category: ${input.category}. Available: ${available}` }],
|
|
1432
|
-
isError: true,
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
const lines = [`# Category: ${input.category}\n`];
|
|
1436
|
-
for (const name of toolNames) {
|
|
1437
|
-
const tool = PINEPAPER_TOOLS.find((t) => t.name === name);
|
|
1438
|
-
if (tool) {
|
|
1439
|
-
lines.push(`## ${tool.name}\n${tool.description}\n`);
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
return {
|
|
1443
|
-
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
|
-
return { content: [{ type: 'text', text: AI_AGENT_GUIDE }] };
|
|
1447
|
-
}
|
|
1448
1321
|
// -----------------------------------------------------------------------
|
|
1449
1322
|
// ASSET TOOLS
|
|
1450
1323
|
// -----------------------------------------------------------------------
|
|
@@ -1660,33 +1533,9 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1660
1533
|
const code = codeGenerator.generateAgentStartJob(input);
|
|
1661
1534
|
const descriptionText = `Started agent job${input.name ? ` "${input.name}"` : ''} with ${input.screenshotPolicy || 'on_complete'} screenshot policy`;
|
|
1662
1535
|
const result = await executeOrGenerate(code, descriptionText, options, 'pinepaper_agent_start_job');
|
|
1663
|
-
//
|
|
1536
|
+
// Append a 1-line workflow hint
|
|
1664
1537
|
if (result.content && result.content.length > 0 && result.content[0].type === 'text') {
|
|
1665
|
-
|
|
1666
|
-
const isDesignSpecific = detectDesignSpecificity(prompt);
|
|
1667
|
-
// Always include workflow hint
|
|
1668
|
-
let guidance = `\n\nNEXT: Call pinepaper_agent_batch_execute with ALL operations in one call:` +
|
|
1669
|
-
`\n 1. set_canvas_size / set_background / execute_generator (canvas setup)` +
|
|
1670
|
-
`\n 2. create (items — text, shapes, etc.)` +
|
|
1671
|
-
`\n 3. animate / keyframe_animate / relation (REQUIRED for animation — add for EACH item)` +
|
|
1672
|
-
`\n 4. play_timeline (REQUIRED — starts playback)` +
|
|
1673
|
-
`\nThen: pinepaper_agent_end_job (returns screenshot for validation)`;
|
|
1674
|
-
// Inject design inspiration when the prompt is vague
|
|
1675
|
-
if (!isDesignSpecific) {
|
|
1676
|
-
guidance += `\n\n─── DESIGN INSPIRATION (your prompt is open-ended — use these for creative direction) ───` +
|
|
1677
|
-
`\nBACKGROUND: Use execute_generator for rich visuals:` +
|
|
1678
|
-
`\n Dreamy/soft: drawBokeh, drawGradientMesh, drawOrganicFlow` +
|
|
1679
|
-
`\n Energetic: drawSunburst, drawSunsetScene, drawGeometricAbstract` +
|
|
1680
|
-
`\n Tech/modern: drawCircuit, drawGrid, drawWindField` +
|
|
1681
|
-
`\n Calm/organic: drawWaves, drawFluidFlow, drawNoiseTexture` +
|
|
1682
|
-
`\nCOLORS: dark bg (#0f172a, #1a1a2e) + bright accents (#f472b6 pink, #818cf8 purple, #fbbf24 gold, #34d399 green)` +
|
|
1683
|
-
`\nCOMPOSITION: generator bg → large decorative shapes (low opacity 0.3-0.5) → main text → small accents on top` +
|
|
1684
|
-
`\nANIMATION: Use "relation" ops for dynamic motion:` +
|
|
1685
|
-
`\n orbits (items circle around center), follows (items trail each other), wave_through (ripple effect)` +
|
|
1686
|
-
`\n Plus: keyframe_animate for timed reveals (fade in, scale up), animate for loops (pulse, rotate, bounce)` +
|
|
1687
|
-
`\n5s TIMING: bg 0s → shapes fade-in 0.5-1.5s → main text 2-3s → accents 3-4s → play_timeline`;
|
|
1688
|
-
}
|
|
1689
|
-
result.content[0].text += guidance;
|
|
1538
|
+
result.content[0].text += `\n\nNEXT: Call batch_execute with all operations, then end_job.`;
|
|
1690
1539
|
}
|
|
1691
1540
|
return result;
|
|
1692
1541
|
}
|
|
@@ -1752,6 +1601,37 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1752
1601
|
}
|
|
1753
1602
|
case 'pinepaper_agent_batch_execute': {
|
|
1754
1603
|
const input = AgentBatchExecuteInputSchema.parse(args);
|
|
1604
|
+
// Ontology preflight — catch typos in itemType/relationType/
|
|
1605
|
+
// effectType/generatorName before generating JS, so the LLM gets a
|
|
1606
|
+
// structured hint instead of a silent failure inside the browser.
|
|
1607
|
+
// Opt-out via skipValidation: true when the caller knows what they're
|
|
1608
|
+
// doing (e.g. experimental vocabulary).
|
|
1609
|
+
if (!input.skipValidation) {
|
|
1610
|
+
const ops = input.operations;
|
|
1611
|
+
const violations = validateBatchVocabulary(ops);
|
|
1612
|
+
if (violations.length > 0) {
|
|
1613
|
+
return errorResult(ErrorCodes.VALIDATION_ERROR, `Batch preflight rejected ${violations.length} operation${violations.length > 1 ? 's' : ''} with unknown vocabulary value${violations.length > 1 ? 's' : ''}. Fix the offending fields and retry, or pass skipValidation: true to bypass.`, violations.map((v) => ({
|
|
1614
|
+
path: `operations[${v.opIndex}].${v.field}`,
|
|
1615
|
+
opType: v.opType,
|
|
1616
|
+
vocabulary: v.hint,
|
|
1617
|
+
})));
|
|
1618
|
+
}
|
|
1619
|
+
// Property-key typo detection — narrow scope (Levenshtein ≤2) so
|
|
1620
|
+
// legitimate extras like `gradient` pass through. Catches the
|
|
1621
|
+
// common typo class (radiues vs radius, fontSiz vs fontSize)
|
|
1622
|
+
// before browser execution silently drops them.
|
|
1623
|
+
const typos = detectBatchPropertyTypos(ops);
|
|
1624
|
+
if (typos.length > 0) {
|
|
1625
|
+
return errorResult(ErrorCodes.VALIDATION_ERROR, `Batch preflight detected ${typos.length} likely property-key typo${typos.length > 1 ? 's' : ''}. Each suggestion is a Levenshtein-≤2 match against the schema for that itemType — fix and retry, or pass skipValidation: true to bypass.`, typos.map((t) => ({
|
|
1626
|
+
path: `operations[${t.opIndex}].properties.${t.property}`,
|
|
1627
|
+
opType: t.opType,
|
|
1628
|
+
itemType: t.itemType,
|
|
1629
|
+
received: t.property,
|
|
1630
|
+
suggestion: t.suggestion,
|
|
1631
|
+
validKeys: t.validKeys,
|
|
1632
|
+
})));
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1755
1635
|
const code = codeGenerator.generateAgentBatchExecute(input);
|
|
1756
1636
|
const description = `Batch executed ${input.operations.length} operations${input.atomic !== false ? ' (atomic)' : ''}`;
|
|
1757
1637
|
// Check for out-of-bounds items before execution
|
|
@@ -1926,140 +1806,9 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1926
1806
|
// -----------------------------------------------------------------------
|
|
1927
1807
|
// MAP TOOLS
|
|
1928
1808
|
// -----------------------------------------------------------------------
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
const description = `Loads ${input.mapId} map with ${input.projection || 'default'} projection`;
|
|
1933
|
-
return executeOrGenerate(code, description, options, 'pinepaper_load_map');
|
|
1934
|
-
}
|
|
1935
|
-
case 'pinepaper_highlight_regions': {
|
|
1936
|
-
const input = HighlightRegionsInputSchema.parse(args);
|
|
1937
|
-
const code = codeGenerator.generateHighlightRegions(input);
|
|
1938
|
-
const description = `Highlights ${input.regionIds.length} region(s) on the map`;
|
|
1939
|
-
return executeOrGenerate(code, description, options, 'pinepaper_highlight_regions');
|
|
1940
|
-
}
|
|
1941
|
-
case 'pinepaper_unhighlight_regions': {
|
|
1942
|
-
const input = UnhighlightRegionsInputSchema.parse(args);
|
|
1943
|
-
const code = codeGenerator.generateUnhighlightRegions(input);
|
|
1944
|
-
return executeOrGenerate(code, 'Removes region highlights', options, 'pinepaper_unhighlight_regions');
|
|
1945
|
-
}
|
|
1946
|
-
case 'pinepaper_apply_data_colors': {
|
|
1947
|
-
const input = ApplyDataColorsInputSchema.parse(args);
|
|
1948
|
-
const code = codeGenerator.generateApplyDataColors(input);
|
|
1949
|
-
const regionCount = Object.keys(input.data).length;
|
|
1950
|
-
const description = `Applies choropleth coloring to ${regionCount} region(s)`;
|
|
1951
|
-
return executeOrGenerate(code, description, options, 'pinepaper_apply_data_colors');
|
|
1952
|
-
}
|
|
1953
|
-
case 'pinepaper_add_marker': {
|
|
1954
|
-
const input = AddMarkerInputSchema.parse(args);
|
|
1955
|
-
const code = codeGenerator.generateAddMarker(input);
|
|
1956
|
-
const description = `Adds marker at [${input.lat}, ${input.lon}]${input.label ? `: ${input.label}` : ''}`;
|
|
1957
|
-
return executeOrGenerate(code, description, options, 'pinepaper_add_marker');
|
|
1958
|
-
}
|
|
1959
|
-
case 'pinepaper_add_map_labels': {
|
|
1960
|
-
const input = AddMapLabelsInputSchema.parse(args);
|
|
1961
|
-
const code = codeGenerator.generateAddMapLabels(input);
|
|
1962
|
-
return executeOrGenerate(code, 'Adds labels to map regions', options, 'pinepaper_add_map_labels');
|
|
1963
|
-
}
|
|
1964
|
-
case 'pinepaper_pan_map': {
|
|
1965
|
-
const input = PanMapInputSchema.parse(args);
|
|
1966
|
-
const code = codeGenerator.generatePanMap(input);
|
|
1967
|
-
const description = `Pans map to [${input.lat}, ${input.lon}]`;
|
|
1968
|
-
return executeOrGenerate(code, description, options, 'pinepaper_pan_map');
|
|
1969
|
-
}
|
|
1970
|
-
case 'pinepaper_zoom_map': {
|
|
1971
|
-
const input = ZoomMapInputSchema.parse(args);
|
|
1972
|
-
const code = codeGenerator.generateZoomMap(input);
|
|
1973
|
-
const description = `Sets map zoom level to ${input.level}`;
|
|
1974
|
-
return executeOrGenerate(code, description, options, 'pinepaper_zoom_map');
|
|
1975
|
-
}
|
|
1976
|
-
case 'pinepaper_export_map': {
|
|
1977
|
-
const code = codeGenerator.generateExportMap();
|
|
1978
|
-
return executeOrGenerate(code, 'Exports map configuration', options, 'pinepaper_export_map');
|
|
1979
|
-
}
|
|
1980
|
-
case 'pinepaper_import_custom_map': {
|
|
1981
|
-
const input = ImportCustomMapInputSchema.parse(args);
|
|
1982
|
-
const code = codeGenerator.generateImportCustomMap(input);
|
|
1983
|
-
const description = input.url ? `Imports custom map from URL` : 'Imports custom GeoJSON map';
|
|
1984
|
-
return executeOrGenerate(code, description, options, 'pinepaper_import_custom_map');
|
|
1985
|
-
}
|
|
1986
|
-
case 'pinepaper_get_region_at_point': {
|
|
1987
|
-
const input = GetRegionAtPointInputSchema.parse(args);
|
|
1988
|
-
const code = codeGenerator.generateGetRegionAtPoint(input);
|
|
1989
|
-
const description = `Gets region at point [${input.x}, ${input.y}]`;
|
|
1990
|
-
return executeOrGenerate(code, description, options, 'pinepaper_get_region_at_point');
|
|
1991
|
-
}
|
|
1992
|
-
// -----------------------------------------------------------------------
|
|
1993
|
-
// MAP ANIMATION/CSV TOOLS
|
|
1994
|
-
// -----------------------------------------------------------------------
|
|
1995
|
-
case 'pinepaper_animate_map_regions': {
|
|
1996
|
-
const input = AnimateMapRegionsInputSchema.parse(args);
|
|
1997
|
-
const code = codeGenerator.generateAnimateMapRegions(input);
|
|
1998
|
-
const regionCount = Object.keys(input.regions).length;
|
|
1999
|
-
const description = `Animates ${regionCount} map region(s) over ${input.duration || 5}s`;
|
|
2000
|
-
return executeOrGenerate(code, description, options, 'pinepaper_animate_map_regions');
|
|
2001
|
-
}
|
|
2002
|
-
case 'pinepaper_animate_map_wave': {
|
|
2003
|
-
const input = AnimateMapWaveInputSchema.parse(args);
|
|
2004
|
-
const code = codeGenerator.generateAnimateMapWave(input);
|
|
2005
|
-
const description = `Creates ${input.waveDirection || 'horizontal'} wave animation`;
|
|
2006
|
-
return executeOrGenerate(code, description, options, 'pinepaper_animate_map_wave');
|
|
2007
|
-
}
|
|
2008
|
-
case 'pinepaper_stop_map_animations': {
|
|
2009
|
-
const input = StopMapAnimationsInputSchema.parse(args);
|
|
2010
|
-
const code = codeGenerator.generateStopMapAnimations(input);
|
|
2011
|
-
return executeOrGenerate(code, 'Stops map region animations', options, 'pinepaper_stop_map_animations');
|
|
2012
|
-
}
|
|
2013
|
-
case 'pinepaper_get_animated_map_regions': {
|
|
2014
|
-
const code = codeGenerator.generateGetAnimatedMapRegions();
|
|
2015
|
-
return executeOrGenerate(code, 'Gets list of animated map regions', options, 'pinepaper_get_animated_map_regions');
|
|
2016
|
-
}
|
|
2017
|
-
case 'pinepaper_export_map_region_csv': {
|
|
2018
|
-
const input = ExportMapRegionCSVInputSchema.parse(args);
|
|
2019
|
-
const code = codeGenerator.generateExportMapRegionCSV(input);
|
|
2020
|
-
return executeOrGenerate(code, 'Exports map region data as CSV', options, 'pinepaper_export_map_region_csv');
|
|
2021
|
-
}
|
|
2022
|
-
case 'pinepaper_import_map_region_csv': {
|
|
2023
|
-
const input = ImportMapRegionCSVInputSchema.parse(args);
|
|
2024
|
-
const code = codeGenerator.generateImportMapRegionCSV(input);
|
|
2025
|
-
return executeOrGenerate(code, 'Imports CSV data to update map regions', options, 'pinepaper_import_map_region_csv');
|
|
2026
|
-
}
|
|
2027
|
-
case 'pinepaper_select_map_regions': {
|
|
2028
|
-
const input = SelectMapRegionsInputSchema.parse(args);
|
|
2029
|
-
const code = codeGenerator.generateSelectMapRegions(input);
|
|
2030
|
-
const description = `Selects ${input.regionIds.length} map region(s)`;
|
|
2031
|
-
return executeOrGenerate(code, description, options, 'pinepaper_select_map_regions');
|
|
2032
|
-
}
|
|
2033
|
-
case 'pinepaper_deselect_map_regions': {
|
|
2034
|
-
const input = DeselectMapRegionsInputSchema.parse(args);
|
|
2035
|
-
const code = codeGenerator.generateDeselectMapRegions(input);
|
|
2036
|
-
return executeOrGenerate(code, 'Deselects map regions', options, 'pinepaper_deselect_map_regions');
|
|
2037
|
-
}
|
|
2038
|
-
case 'pinepaper_get_highlighted_map_regions': {
|
|
2039
|
-
const code = codeGenerator.generateGetHighlightedMapRegions();
|
|
2040
|
-
return executeOrGenerate(code, 'Gets list of highlighted map regions', options, 'pinepaper_get_highlighted_map_regions');
|
|
2041
|
-
}
|
|
2042
|
-
case 'pinepaper_export_map_geojson': {
|
|
2043
|
-
const code = codeGenerator.generateExportMapGeoJson({
|
|
2044
|
-
includeStyles: args.includeStyles,
|
|
2045
|
-
includeMetadata: args.includeMetadata,
|
|
2046
|
-
selectedOnly: args.selectedOnly,
|
|
2047
|
-
download: args.download,
|
|
2048
|
-
filename: args.filename,
|
|
2049
|
-
});
|
|
2050
|
-
return executeOrGenerate(code, 'Exports map as GeoJSON', options, 'pinepaper_export_map_geojson');
|
|
2051
|
-
}
|
|
2052
|
-
case 'pinepaper_export_original_map_geojson': {
|
|
2053
|
-
const code = codeGenerator.generateExportOriginalMapGeoJson({
|
|
2054
|
-
download: args.download,
|
|
2055
|
-
filename: args.filename,
|
|
2056
|
-
});
|
|
2057
|
-
return executeOrGenerate(code, 'Exports original map GeoJSON', options, 'pinepaper_export_original_map_geojson');
|
|
2058
|
-
}
|
|
2059
|
-
case 'pinepaper_get_map_source_info': {
|
|
2060
|
-
const code = codeGenerator.generateGetMapSourceInfo();
|
|
2061
|
-
return executeOrGenerate(code, 'Gets map source information', options, 'pinepaper_get_map_source_info');
|
|
2062
|
-
}
|
|
1809
|
+
// -----------------------------------------------------------------------
|
|
1810
|
+
// MAP TOOLS — extracted to src/tools/handlers/maps.ts
|
|
1811
|
+
// -----------------------------------------------------------------------
|
|
2063
1812
|
case 'pinepaper_register_item': {
|
|
2064
1813
|
const code = codeGenerator.generateRegisterItem({
|
|
2065
1814
|
itemJson: args.itemJson,
|
|
@@ -2085,13 +1834,16 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2085
1834
|
return executeOrGenerate(code, 'Imports image', options, 'pinepaper_import_image');
|
|
2086
1835
|
}
|
|
2087
1836
|
// -----------------------------------------------------------------------
|
|
1837
|
+
// ONTOLOGY TOOLS — extracted to src/tools/handlers/ontology.ts
|
|
1838
|
+
// -----------------------------------------------------------------------
|
|
1839
|
+
// -----------------------------------------------------------------------
|
|
2088
1840
|
// UNKNOWN TOOL
|
|
2089
1841
|
// -----------------------------------------------------------------------
|
|
2090
1842
|
default: {
|
|
2091
1843
|
const message = i18n
|
|
2092
1844
|
? i18n.getError('unknownTool', { toolName })
|
|
2093
1845
|
: `Unknown tool: ${toolName}`;
|
|
2094
|
-
return errorResult(
|
|
1846
|
+
return errorResult(ErrorCodes.UNKNOWN_TOOL, message, {
|
|
2095
1847
|
availableTools: [
|
|
2096
1848
|
'pinepaper_create_item',
|
|
2097
1849
|
'pinepaper_modify_item',
|
|
@@ -2120,12 +1872,7 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2120
1872
|
'pinepaper_get_mask_types',
|
|
2121
1873
|
'pinepaper_get_mask_animations',
|
|
2122
1874
|
'pinepaper_camera_animate',
|
|
2123
|
-
'
|
|
2124
|
-
'pinepaper_camera_pan',
|
|
2125
|
-
'pinepaper_camera_move_to',
|
|
2126
|
-
'pinepaper_camera_reset',
|
|
2127
|
-
'pinepaper_camera_stop',
|
|
2128
|
-
'pinepaper_camera_state',
|
|
1875
|
+
'pinepaper_camera',
|
|
2129
1876
|
'pinepaper_get_items',
|
|
2130
1877
|
'pinepaper_get_relation_stats',
|
|
2131
1878
|
'pinepaper_set_background_color',
|
|
@@ -2134,6 +1881,7 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2134
1881
|
'pinepaper_clear_canvas',
|
|
2135
1882
|
'pinepaper_refresh_page',
|
|
2136
1883
|
'pinepaper_import_svg',
|
|
1884
|
+
'pinepaper_import_mermaid',
|
|
2137
1885
|
'pinepaper_add_filter',
|
|
2138
1886
|
'pinepaper_export_svg',
|
|
2139
1887
|
'pinepaper_export_training_data',
|
|
@@ -2159,6 +1907,8 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2159
1907
|
'pinepaper_diagram_mode',
|
|
2160
1908
|
// Scene tools
|
|
2161
1909
|
'pinepaper_create_scene',
|
|
1910
|
+
'pinepaper_manage_scenes',
|
|
1911
|
+
'pinepaper_scene_playback',
|
|
2162
1912
|
// Agent flow mode tools
|
|
2163
1913
|
'pinepaper_agent_start_job',
|
|
2164
1914
|
'pinepaper_agent_end_job',
|
|
@@ -2180,48 +1930,13 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2180
1930
|
'pinepaper_get_letter_collage_options',
|
|
2181
1931
|
// Canvas presets tools
|
|
2182
1932
|
'pinepaper_get_canvas_presets',
|
|
2183
|
-
// Map tools
|
|
2184
|
-
'
|
|
2185
|
-
'
|
|
2186
|
-
'
|
|
2187
|
-
'
|
|
2188
|
-
'pinepaper_add_marker',
|
|
2189
|
-
'pinepaper_add_map_labels',
|
|
2190
|
-
'pinepaper_pan_map',
|
|
2191
|
-
'pinepaper_zoom_map',
|
|
2192
|
-
'pinepaper_export_map',
|
|
2193
|
-
'pinepaper_import_custom_map',
|
|
2194
|
-
'pinepaper_get_region_at_point',
|
|
2195
|
-
// Map animation/CSV tools
|
|
2196
|
-
'pinepaper_animate_map_regions',
|
|
2197
|
-
'pinepaper_animate_map_wave',
|
|
2198
|
-
'pinepaper_stop_map_animations',
|
|
2199
|
-
'pinepaper_get_animated_map_regions',
|
|
2200
|
-
'pinepaper_export_map_region_csv',
|
|
2201
|
-
'pinepaper_import_map_region_csv',
|
|
2202
|
-
'pinepaper_select_map_regions',
|
|
2203
|
-
'pinepaper_deselect_map_regions',
|
|
2204
|
-
'pinepaper_get_highlighted_map_regions',
|
|
2205
|
-
'pinepaper_export_map_geojson',
|
|
2206
|
-
'pinepaper_export_original_map_geojson',
|
|
2207
|
-
'pinepaper_get_map_source_info',
|
|
1933
|
+
// Map tools (4 action-dispatched)
|
|
1934
|
+
'pinepaper_map',
|
|
1935
|
+
'pinepaper_map_regions',
|
|
1936
|
+
'pinepaper_map_animation',
|
|
1937
|
+
'pinepaper_map_data',
|
|
2208
1938
|
// Font tools
|
|
2209
|
-
'
|
|
2210
|
-
'pinepaper_font_set_name',
|
|
2211
|
-
'pinepaper_font_get_required_chars',
|
|
2212
|
-
'pinepaper_font_get_status',
|
|
2213
|
-
'pinepaper_font_create_glyph',
|
|
2214
|
-
'pinepaper_font_create_space',
|
|
2215
|
-
'pinepaper_font_remove_glyph',
|
|
2216
|
-
'pinepaper_font_set_metrics',
|
|
2217
|
-
'pinepaper_font_export',
|
|
2218
|
-
'pinepaper_font_load_into_document',
|
|
2219
|
-
'pinepaper_font_export_data',
|
|
2220
|
-
'pinepaper_font_import_data',
|
|
2221
|
-
'pinepaper_font_clear',
|
|
2222
|
-
'pinepaper_font_remove_overlap',
|
|
2223
|
-
'pinepaper_font_correct_direction',
|
|
2224
|
-
'pinepaper_font_cleanup_path',
|
|
1939
|
+
'pinepaper_font',
|
|
2225
1940
|
// Template tools
|
|
2226
1941
|
'pinepaper_apply_template',
|
|
2227
1942
|
// Image import tools
|
|
@@ -2231,6 +1946,38 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2231
1946
|
// On-demand guide & runtime config
|
|
2232
1947
|
'pinepaper_tool_guide',
|
|
2233
1948
|
'pinepaper_set_toolkit',
|
|
1949
|
+
// Ontology tools
|
|
1950
|
+
'pinepaper_get_canvas_ontology',
|
|
1951
|
+
'pinepaper_analyze_design',
|
|
1952
|
+
'pinepaper_validate_design',
|
|
1953
|
+
'pinepaper_query_ontology',
|
|
1954
|
+
// Selection, transform & history tools
|
|
1955
|
+
'pinepaper_selection',
|
|
1956
|
+
'pinepaper_transform',
|
|
1957
|
+
'pinepaper_history',
|
|
1958
|
+
// Image processing tools
|
|
1959
|
+
'pinepaper_image_filter',
|
|
1960
|
+
'pinepaper_lasso',
|
|
1961
|
+
'pinepaper_cutout_style',
|
|
1962
|
+
// Composition & view tools
|
|
1963
|
+
'pinepaper_precomp',
|
|
1964
|
+
'pinepaper_view',
|
|
1965
|
+
'pinepaper_background',
|
|
1966
|
+
// Canvas query tools
|
|
1967
|
+
'pinepaper_query',
|
|
1968
|
+
// Deformation, sprite sheets, storage, interaction
|
|
1969
|
+
'pinepaper_deform',
|
|
1970
|
+
'pinepaper_sprite_sheet',
|
|
1971
|
+
'pinepaper_storage',
|
|
1972
|
+
'pinepaper_interaction',
|
|
1973
|
+
// Widget export
|
|
1974
|
+
'pinepaper_export_widget',
|
|
1975
|
+
'pinepaper_export_widget_html',
|
|
1976
|
+
// Data visualization, magic, physics, measurement
|
|
1977
|
+
'pinepaper_create_chart',
|
|
1978
|
+
'pinepaper_magic',
|
|
1979
|
+
'pinepaper_physics',
|
|
1980
|
+
'pinepaper_measurement',
|
|
2234
1981
|
],
|
|
2235
1982
|
});
|
|
2236
1983
|
}
|