@pinepaper.studio/mcp-server 1.5.3 → 1.5.5
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/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/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -4
- package/dist/index.js.map +1 -1
- 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/vocabulary.d.ts.map +1 -1
- package/dist/ontology/vocabulary.js +34 -0
- package/dist/ontology/vocabulary.js.map +1 -1
- 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 +3 -0
- package/dist/tools/compact-descriptions.d.ts.map +1 -1
- package/dist/tools/compact-descriptions.js +17 -9
- 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 +1251 -2327
- 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 +100 -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 +213 -695
- 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 +45 -66
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +36 -62
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +35 -34
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +869 -474
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +1047 -988
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +282 -84
- package/dist/types/schemas.js.map +1 -1
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +18 -0
- package/dist/version.js.map +1 -0
- package/package.json +1 -1
package/dist/tools/handlers.js
CHANGED
|
@@ -6,45 +6,52 @@
|
|
|
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
|
|
13
13
|
AgentStartJobInputSchema, AgentEndJobInputSchema, AgentResetInputSchema, AgentBatchExecuteInputSchema, AgentExportInputSchema, AgentAnalyzeInputSchema,
|
|
14
|
-
// Interactive/Trigger schemas
|
|
15
|
-
AddTriggerInputSchema, RemoveTriggerInputSchema, QueryTriggersInputSchema,
|
|
16
|
-
// Quiz schemas
|
|
17
|
-
CreateQuizInputSchema, GetQuizStateInputSchema, ResetQuizInputSchema,
|
|
18
14
|
// 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,
|
|
15
|
+
CreateLetterCollageInputSchema, AnimateLetterCollageInputSchema, ApplyTemplateInputSchema,
|
|
24
16
|
// Custom relation/code schemas
|
|
25
17
|
RegisterCustomRelationInputSchema, ExecuteCustomCodeInputSchema,
|
|
26
18
|
// Mask schemas
|
|
27
19
|
ApplyAnimatedMaskInputSchema, ApplyCustomMaskInputSchema, RemoveMaskInputSchema,
|
|
28
|
-
// Template schemas
|
|
29
|
-
ApplyTemplateInputSchema,
|
|
30
20
|
// Image import schemas
|
|
31
21
|
ImportImageInputSchema,
|
|
32
|
-
// Tool guide schema
|
|
33
|
-
ToolGuideInputSchema,
|
|
34
|
-
// Ontology schemas
|
|
35
|
-
AnalyzeDesignInputSchema, ValidateDesignInputSchema, QueryOntologyInputSchema,
|
|
36
22
|
// Scene management schemas
|
|
37
23
|
ManageScenesInputSchema, ScenePlaybackInputSchema,
|
|
38
24
|
// New consolidated tool schemas
|
|
39
|
-
SelectionInputSchema, TransformInputSchema, HistoryInputSchema, ImageFilterInputSchema, LassoInputSchema, CutoutStyleInputSchema, PrecompInputSchema,
|
|
25
|
+
SelectionInputSchema, TransformInputSchema, HistoryInputSchema, ImageFilterInputSchema, LassoInputSchema, CutoutStyleInputSchema, PrecompInputSchema, BackgroundInputSchema, QueryInputSchema, DeformInputSchema, SpriteSheetInputSchema, InteractionInputSchema, ExportWidgetInputSchema, ExportWidgetHtmlInputSchema, CreateChartInputSchema, MagicInputSchema, PhysicsInputSchema, MeasurementInputSchema, ErrorCodes, } from '../types/schemas.js';
|
|
40
26
|
import { ZodError } from 'zod';
|
|
41
27
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
42
28
|
import { tmpdir } from 'node:os';
|
|
43
29
|
import { join } from 'node:path';
|
|
44
30
|
import { getBrowserController, resetBrowserController, } from '../browser/puppeteer-controller.js';
|
|
31
|
+
import { sameStudioTarget, validateStudioUrl } from '../browser/url-target.js';
|
|
45
32
|
import { getPerformanceTracker } from '../metrics/index.js';
|
|
46
33
|
import { formatErrorContext, captureCanvasState } from '../execution/index.js';
|
|
47
34
|
import { getSessionManager } from '../agent/session-manager.js';
|
|
35
|
+
import { vocabularyHintForPath, validateBatchVocabulary, detectBatchPropertyTypos } from '../ontology/hints.js';
|
|
36
|
+
import { cameraHandlers } from './handlers/camera.js';
|
|
37
|
+
import { fontHandlers } from './handlers/font.js';
|
|
38
|
+
import { toolGuideHandlers } from './handlers/tool-guide.js';
|
|
39
|
+
import { mapHandlers } from './handlers/maps.js';
|
|
40
|
+
import { ontologyHandlers } from './handlers/ontology.js';
|
|
41
|
+
import { exportHandlers } from './handlers/export.js';
|
|
42
|
+
/**
|
|
43
|
+
* Registry of per-domain handler maps. Tools listed here short-circuit the
|
|
44
|
+
* main switch at dispatch time (see handleToolCallInner). New domains are
|
|
45
|
+
* folded in incrementally as they are migrated out of the monolithic switch.
|
|
46
|
+
*/
|
|
47
|
+
const DOMAIN_HANDLERS = [
|
|
48
|
+
cameraHandlers,
|
|
49
|
+
fontHandlers,
|
|
50
|
+
toolGuideHandlers,
|
|
51
|
+
mapHandlers,
|
|
52
|
+
ontologyHandlers,
|
|
53
|
+
exportHandlers,
|
|
54
|
+
];
|
|
48
55
|
/**
|
|
49
56
|
* Get screenshot mode from environment variable.
|
|
50
57
|
* Defaults to 'on_request' for optimal performance.
|
|
@@ -146,7 +153,7 @@ function screenshotResult(screenshot) {
|
|
|
146
153
|
],
|
|
147
154
|
};
|
|
148
155
|
}
|
|
149
|
-
function errorResult(code, message, details, context) {
|
|
156
|
+
export function errorResult(code, message, details, context) {
|
|
150
157
|
// Build error object
|
|
151
158
|
const errorObj = {
|
|
152
159
|
success: false,
|
|
@@ -173,10 +180,18 @@ function handleValidationError(error, i18n) {
|
|
|
173
180
|
const message = i18n
|
|
174
181
|
? i18n.getError('validationError', { message: 'Invalid input parameters' })
|
|
175
182
|
: 'Invalid input parameters';
|
|
176
|
-
|
|
177
|
-
path
|
|
178
|
-
message: e.message
|
|
179
|
-
|
|
183
|
+
const details = error.errors.map((e) => {
|
|
184
|
+
const path = e.path.join('.');
|
|
185
|
+
const detail = { path, message: e.message };
|
|
186
|
+
if (e.code === 'invalid_enum_value') {
|
|
187
|
+
const received = e.received;
|
|
188
|
+
const hint = vocabularyHintForPath(path, received);
|
|
189
|
+
if (hint)
|
|
190
|
+
detail.vocabulary = hint;
|
|
191
|
+
}
|
|
192
|
+
return detail;
|
|
193
|
+
});
|
|
194
|
+
return errorResult(ErrorCodes.VALIDATION_ERROR, message, details);
|
|
180
195
|
}
|
|
181
196
|
/**
|
|
182
197
|
* Execute code in browser if connected, otherwise return generated code.
|
|
@@ -192,7 +207,7 @@ function handleValidationError(error, i18n) {
|
|
|
192
207
|
*
|
|
193
208
|
* Default is 'on_request' for optimal performance per best practices.
|
|
194
209
|
*/
|
|
195
|
-
async function executeOrGenerate(code, description, options, toolName) {
|
|
210
|
+
export async function executeOrGenerate(code, description, options, toolName) {
|
|
196
211
|
const { executeInBrowser, browserController, screenshotMode, executionMode } = options;
|
|
197
212
|
const effectiveExecutionMode = executionMode ?? getExecutionMode();
|
|
198
213
|
const effectiveScreenshotMode = screenshotMode ?? getScreenshotMode();
|
|
@@ -369,7 +384,7 @@ ${code}
|
|
|
369
384
|
// =============================================================================
|
|
370
385
|
// I18N HELPER FUNCTIONS
|
|
371
386
|
// =============================================================================
|
|
372
|
-
function getLocalizedSuccessMessage(i18n, key, params) {
|
|
387
|
+
export function getLocalizedSuccessMessage(i18n, key, params) {
|
|
373
388
|
if (!i18n) {
|
|
374
389
|
// Fallback to English template
|
|
375
390
|
return Object.entries(params).reduce((msg, [k, v]) => msg.replace(`{{${k}}}`, String(v)), key);
|
|
@@ -453,6 +468,14 @@ export async function handleToolCall(toolName, args, options = {}) {
|
|
|
453
468
|
async function handleToolCallInner(toolName, args, options, tracker, baseTimerId) {
|
|
454
469
|
const { i18n } = options;
|
|
455
470
|
try {
|
|
471
|
+
// Domain-module dispatch: tools migrated to per-domain handler files
|
|
472
|
+
// short-circuit before the main switch. See DOMAIN_HANDLERS for the
|
|
473
|
+
// current registry.
|
|
474
|
+
for (const domainMap of DOMAIN_HANDLERS) {
|
|
475
|
+
const handler = domainMap[toolName];
|
|
476
|
+
if (handler)
|
|
477
|
+
return await handler(args, options);
|
|
478
|
+
}
|
|
456
479
|
switch (toolName) {
|
|
457
480
|
// -----------------------------------------------------------------------
|
|
458
481
|
// ITEM TOOLS
|
|
@@ -493,7 +516,7 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
493
516
|
const animProps = ['animationType', 'animationSpeed', 'keyframes', 'animation'];
|
|
494
517
|
const passedAnimProps = animProps.filter(p => p in (input.properties || {}));
|
|
495
518
|
if (passedAnimProps.length > 0) {
|
|
496
|
-
return errorResult(
|
|
519
|
+
return errorResult(ErrorCodes.INVALID_PROPERTIES, `Animation properties (${passedAnimProps.join(', ')}) cannot be set via pinepaper_modify_item — they will be silently ignored. ` +
|
|
497
520
|
`Use pinepaper_animate for loop animations (pulse, rotate, bounce, fade, wobble, slide) ` +
|
|
498
521
|
`or pinepaper_keyframe_animate for timed/sequenced animations with precise control.`);
|
|
499
522
|
}
|
|
@@ -589,11 +612,6 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
589
612
|
const code = codeGenerator.generatePrecomp(input);
|
|
590
613
|
return executeOrGenerate(code, `Precomp: ${input.action}`, options, 'pinepaper_precomp');
|
|
591
614
|
}
|
|
592
|
-
case 'pinepaper_view': {
|
|
593
|
-
const input = ViewInputSchema.parse(args);
|
|
594
|
-
const code = codeGenerator.generateView(input);
|
|
595
|
-
return executeOrGenerate(code, `View: ${input.action}`, options, 'pinepaper_view');
|
|
596
|
-
}
|
|
597
615
|
case 'pinepaper_background': {
|
|
598
616
|
const input = BackgroundInputSchema.parse(args);
|
|
599
617
|
const code = codeGenerator.generateBackground(input);
|
|
@@ -608,6 +626,57 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
608
626
|
return executeOrGenerate(code, `Query: ${input.action}`, options, 'pinepaper_query');
|
|
609
627
|
}
|
|
610
628
|
// -----------------------------------------------------------------------
|
|
629
|
+
// DEFORMATION, SPRITE SHEETS, STORAGE, INTERACTION
|
|
630
|
+
// -----------------------------------------------------------------------
|
|
631
|
+
case 'pinepaper_deform': {
|
|
632
|
+
const input = DeformInputSchema.parse(args);
|
|
633
|
+
const code = codeGenerator.generateDeform(input);
|
|
634
|
+
return executeOrGenerate(code, `Deform: ${input.action}`, options, 'pinepaper_deform');
|
|
635
|
+
}
|
|
636
|
+
case 'pinepaper_sprite_sheet': {
|
|
637
|
+
const input = SpriteSheetInputSchema.parse(args);
|
|
638
|
+
const code = codeGenerator.generateSpriteSheet(input);
|
|
639
|
+
return executeOrGenerate(code, `Sprite sheet: ${input.action}`, options, 'pinepaper_sprite_sheet');
|
|
640
|
+
}
|
|
641
|
+
case 'pinepaper_interaction': {
|
|
642
|
+
const input = InteractionInputSchema.parse(args);
|
|
643
|
+
const code = codeGenerator.generateInteraction(input);
|
|
644
|
+
return executeOrGenerate(code, `Interaction: ${input.action}`, options, 'pinepaper_interaction');
|
|
645
|
+
}
|
|
646
|
+
case 'pinepaper_export_widget': {
|
|
647
|
+
const input = ExportWidgetInputSchema.parse(args);
|
|
648
|
+
const code = codeGenerator.generateExportWidget(input);
|
|
649
|
+
return executeOrGenerate(code, 'Export widget JSON', options, 'pinepaper_export_widget');
|
|
650
|
+
}
|
|
651
|
+
case 'pinepaper_export_widget_html': {
|
|
652
|
+
const input = ExportWidgetHtmlInputSchema.parse(args);
|
|
653
|
+
const code = codeGenerator.generateExportWidgetHtml(input);
|
|
654
|
+
return executeOrGenerate(code, 'Export widget HTML', options, 'pinepaper_export_widget_html');
|
|
655
|
+
}
|
|
656
|
+
// -----------------------------------------------------------------------
|
|
657
|
+
// DATA VISUALIZATION, MAGIC, PHYSICS, MEASUREMENT
|
|
658
|
+
// -----------------------------------------------------------------------
|
|
659
|
+
case 'pinepaper_create_chart': {
|
|
660
|
+
const input = CreateChartInputSchema.parse(args);
|
|
661
|
+
const code = codeGenerator.generateChart(input);
|
|
662
|
+
return executeOrGenerate(code, `Chart: ${input.action}`, options, 'pinepaper_create_chart');
|
|
663
|
+
}
|
|
664
|
+
case 'pinepaper_magic': {
|
|
665
|
+
const input = MagicInputSchema.parse(args);
|
|
666
|
+
const code = codeGenerator.generateMagic(input);
|
|
667
|
+
return executeOrGenerate(code, `Magic: ${input.action}`, options, 'pinepaper_magic');
|
|
668
|
+
}
|
|
669
|
+
case 'pinepaper_physics': {
|
|
670
|
+
const input = PhysicsInputSchema.parse(args);
|
|
671
|
+
const code = codeGenerator.generatePhysics(input);
|
|
672
|
+
return executeOrGenerate(code, `Physics: ${input.action}`, options, 'pinepaper_physics');
|
|
673
|
+
}
|
|
674
|
+
case 'pinepaper_measurement': {
|
|
675
|
+
const input = MeasurementInputSchema.parse(args);
|
|
676
|
+
const code = codeGenerator.generateMeasurement(input);
|
|
677
|
+
return executeOrGenerate(code, `Measurement: ${input.action}`, options, 'pinepaper_measurement');
|
|
678
|
+
}
|
|
679
|
+
// -----------------------------------------------------------------------
|
|
611
680
|
// BATCH OPERATION TOOLS
|
|
612
681
|
// -----------------------------------------------------------------------
|
|
613
682
|
case 'pinepaper_batch_create': {
|
|
@@ -750,168 +819,11 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
750
819
|
return executeOrGenerate(code, 'Gets available mask animation presets', options, 'pinepaper_get_mask_animations');
|
|
751
820
|
}
|
|
752
821
|
// -----------------------------------------------------------------------
|
|
753
|
-
// CAMERA TOOLS
|
|
754
|
-
// -----------------------------------------------------------------------
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
const loop = args.loop ?? false;
|
|
759
|
-
const delay = args.delay ?? 0;
|
|
760
|
-
const keyframesStr = JSON.stringify(keyframes);
|
|
761
|
-
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} });`;
|
|
762
|
-
return executeOrGenerate(code, `Animates camera with ${keyframes.length} keyframes over ${duration}s`, options, 'pinepaper_camera_animate');
|
|
763
|
-
}
|
|
764
|
-
case 'pinepaper_camera_zoom': {
|
|
765
|
-
const direction = args.direction;
|
|
766
|
-
const level = args.level ?? (direction === 'in' ? 2 : 0.5);
|
|
767
|
-
const duration = args.duration ?? 0.5;
|
|
768
|
-
const method = direction === 'in' ? 'zoomIn' : 'zoomOut';
|
|
769
|
-
const code = `app.camera && app.camera.${method} ? app.camera.${method}(${level}, ${duration}) : null;`;
|
|
770
|
-
return executeOrGenerate(code, `Camera zoom ${direction} to ${level}x`, options, 'pinepaper_camera_zoom');
|
|
771
|
-
}
|
|
772
|
-
case 'pinepaper_camera_pan': {
|
|
773
|
-
const direction = args.direction;
|
|
774
|
-
const amount = args.amount ?? 100;
|
|
775
|
-
const x = args.x;
|
|
776
|
-
const y = args.y;
|
|
777
|
-
const duration = args.duration ?? 0.5;
|
|
778
|
-
let code;
|
|
779
|
-
let description;
|
|
780
|
-
if (x !== undefined && y !== undefined) {
|
|
781
|
-
code = `app.camera && app.camera.panTo ? app.camera.panTo(${x}, ${y}, ${duration}) : null;`;
|
|
782
|
-
description = `Camera pan to (${x}, ${y})`;
|
|
783
|
-
}
|
|
784
|
-
else if (direction) {
|
|
785
|
-
const methodMap = { left: 'panLeft', right: 'panRight', up: 'panUp', down: 'panDown' };
|
|
786
|
-
const method = methodMap[direction];
|
|
787
|
-
code = `app.camera && app.camera.${method} ? app.camera.${method}(${amount}, ${duration}) : null;`;
|
|
788
|
-
description = `Camera pan ${direction} by ${amount}px`;
|
|
789
|
-
}
|
|
790
|
-
else {
|
|
791
|
-
code = `// No direction or coordinates specified`;
|
|
792
|
-
description = 'Camera pan (no parameters)';
|
|
793
|
-
}
|
|
794
|
-
return executeOrGenerate(code, description, options, 'pinepaper_camera_pan');
|
|
795
|
-
}
|
|
796
|
-
case 'pinepaper_camera_move_to': {
|
|
797
|
-
const x = args.x;
|
|
798
|
-
const y = args.y;
|
|
799
|
-
const zoom = args.zoom;
|
|
800
|
-
const duration = args.duration ?? 0.5;
|
|
801
|
-
const code = `app.camera && app.camera.moveTo ? app.camera.moveTo(${x}, ${y}, ${zoom}, ${duration}) : null;`;
|
|
802
|
-
return executeOrGenerate(code, `Camera move to (${x}, ${y}) at ${zoom}x zoom`, options, 'pinepaper_camera_move_to');
|
|
803
|
-
}
|
|
804
|
-
case 'pinepaper_camera_reset': {
|
|
805
|
-
const duration = args.duration ?? 0.5;
|
|
806
|
-
const code = `app.camera && app.camera.reset ? app.camera.reset(${duration}) : null;`;
|
|
807
|
-
return executeOrGenerate(code, 'Reset camera to default state', options, 'pinepaper_camera_reset');
|
|
808
|
-
}
|
|
809
|
-
case 'pinepaper_camera_stop': {
|
|
810
|
-
const code = `app.camera && app.camera.stop ? app.camera.stop() : null;`;
|
|
811
|
-
return executeOrGenerate(code, 'Stop camera animation', options, 'pinepaper_camera_stop');
|
|
812
|
-
}
|
|
813
|
-
case 'pinepaper_camera_state': {
|
|
814
|
-
const code = `app.camera && app.camera.getState ? app.camera.getState() : { zoom: 1, center: [400, 300], isAnimating: false };`;
|
|
815
|
-
return executeOrGenerate(code, 'Get current camera state', options, 'pinepaper_camera_state');
|
|
816
|
-
}
|
|
817
|
-
// -----------------------------------------------------------------------
|
|
818
|
-
// FONT TOOLS
|
|
819
|
-
// -----------------------------------------------------------------------
|
|
820
|
-
case 'pinepaper_font_show_studio': {
|
|
821
|
-
const code = `app.fontStudio && app.fontStudio.show ? app.fontStudio.show() : null;`;
|
|
822
|
-
return executeOrGenerate(code, 'Opens Font Studio UI', options, 'pinepaper_font_show_studio');
|
|
823
|
-
}
|
|
824
|
-
case 'pinepaper_font_set_name': {
|
|
825
|
-
const { name } = args;
|
|
826
|
-
const code = `app.fontStudio.setName(${JSON.stringify(name)});`;
|
|
827
|
-
return executeOrGenerate(code, `Set font name to "${name}"`, options, 'pinepaper_font_set_name');
|
|
828
|
-
}
|
|
829
|
-
case 'pinepaper_font_get_required_chars': {
|
|
830
|
-
const { set } = args;
|
|
831
|
-
const setArg = set ? JSON.stringify(set) : '"minimum"';
|
|
832
|
-
const code = `app.fontStudio.getRequiredChars(${setArg});`;
|
|
833
|
-
return executeOrGenerate(code, `Get required characters (${set || 'minimum'} set)`, options, 'pinepaper_font_get_required_chars');
|
|
834
|
-
}
|
|
835
|
-
case 'pinepaper_font_get_status': {
|
|
836
|
-
const code = `app.fontStudio.getStatus();`;
|
|
837
|
-
return executeOrGenerate(code, 'Get font completion status', options, 'pinepaper_font_get_status');
|
|
838
|
-
}
|
|
839
|
-
case 'pinepaper_font_create_glyph': {
|
|
840
|
-
const { character, pathId } = args;
|
|
841
|
-
const code = `app.fontStudio.createGlyph(${JSON.stringify(character)}, ${JSON.stringify(pathId)});`;
|
|
842
|
-
return executeOrGenerate(code, `Create glyph for "${character}" from path ${pathId}`, options, 'pinepaper_font_create_glyph');
|
|
843
|
-
}
|
|
844
|
-
case 'pinepaper_font_create_space': {
|
|
845
|
-
const { width } = args;
|
|
846
|
-
const code = width !== undefined
|
|
847
|
-
? `app.fontStudio.createSpace(${width});`
|
|
848
|
-
: `app.fontStudio.createSpace();`;
|
|
849
|
-
return executeOrGenerate(code, `Create space glyph${width ? ` (width: ${width})` : ''}`, options, 'pinepaper_font_create_space');
|
|
850
|
-
}
|
|
851
|
-
case 'pinepaper_font_remove_glyph': {
|
|
852
|
-
const { character } = args;
|
|
853
|
-
const code = `app.fontStudio.removeGlyph(${JSON.stringify(character)});`;
|
|
854
|
-
return executeOrGenerate(code, `Remove glyph for "${character}"`, options, 'pinepaper_font_remove_glyph');
|
|
855
|
-
}
|
|
856
|
-
case 'pinepaper_font_set_metrics': {
|
|
857
|
-
const metrics = args;
|
|
858
|
-
const code = `app.fontStudio.setMetrics(${JSON.stringify(metrics)});`;
|
|
859
|
-
return executeOrGenerate(code, 'Set font metrics', options, 'pinepaper_font_set_metrics');
|
|
860
|
-
}
|
|
861
|
-
case 'pinepaper_font_export': {
|
|
862
|
-
const { download } = args;
|
|
863
|
-
const code = download === false
|
|
864
|
-
? `app.fontStudio.export({ download: false });`
|
|
865
|
-
: `app.fontStudio.export();`;
|
|
866
|
-
return executeOrGenerate(code, 'Export font as OTF', options, 'pinepaper_font_export');
|
|
867
|
-
}
|
|
868
|
-
case 'pinepaper_font_load_into_document': {
|
|
869
|
-
const code = `app.fontStudio.loadIntoDocument();`;
|
|
870
|
-
return executeOrGenerate(code, 'Load font into document', options, 'pinepaper_font_load_into_document');
|
|
871
|
-
}
|
|
872
|
-
case 'pinepaper_font_export_data': {
|
|
873
|
-
const { download } = args;
|
|
874
|
-
const code = download === false
|
|
875
|
-
? `app.fontStudio.exportData({ download: false });`
|
|
876
|
-
: `app.fontStudio.exportData();`;
|
|
877
|
-
return executeOrGenerate(code, 'Export font data as JSON', options, 'pinepaper_font_export_data');
|
|
878
|
-
}
|
|
879
|
-
case 'pinepaper_font_import_data': {
|
|
880
|
-
const { data } = args;
|
|
881
|
-
const code = `app.fontStudio.importData(${JSON.stringify(data)});`;
|
|
882
|
-
return executeOrGenerate(code, 'Import font data from JSON', options, 'pinepaper_font_import_data');
|
|
883
|
-
}
|
|
884
|
-
case 'pinepaper_font_clear': {
|
|
885
|
-
const code = `app.fontStudio.clear();`;
|
|
886
|
-
return executeOrGenerate(code, 'Clear all glyphs and reset font', options, 'pinepaper_font_clear');
|
|
887
|
-
}
|
|
888
|
-
case 'pinepaper_font_remove_overlap': {
|
|
889
|
-
const { pathId } = args;
|
|
890
|
-
const code = `app.fontStudio.removeOverlap(${JSON.stringify(pathId)});`;
|
|
891
|
-
return executeOrGenerate(code, `Remove overlaps from path ${pathId}`, options, 'pinepaper_font_remove_overlap');
|
|
892
|
-
}
|
|
893
|
-
case 'pinepaper_font_correct_direction': {
|
|
894
|
-
const { pathId } = args;
|
|
895
|
-
const code = `app.fontStudio.correctDirection(${JSON.stringify(pathId)});`;
|
|
896
|
-
return executeOrGenerate(code, `Correct path direction for ${pathId}`, options, 'pinepaper_font_correct_direction');
|
|
897
|
-
}
|
|
898
|
-
case 'pinepaper_font_cleanup_path': {
|
|
899
|
-
const { pathId, removeOverlap, correctDirection, smooth, smoothTolerance } = args;
|
|
900
|
-
const optsObj = {};
|
|
901
|
-
if (removeOverlap !== undefined)
|
|
902
|
-
optsObj.removeOverlap = removeOverlap;
|
|
903
|
-
if (correctDirection !== undefined)
|
|
904
|
-
optsObj.correctDirection = correctDirection;
|
|
905
|
-
if (smooth !== undefined)
|
|
906
|
-
optsObj.smooth = smooth;
|
|
907
|
-
if (smoothTolerance !== undefined)
|
|
908
|
-
optsObj.smoothTolerance = smoothTolerance;
|
|
909
|
-
const hasOpts = Object.keys(optsObj).length > 0;
|
|
910
|
-
const code = hasOpts
|
|
911
|
-
? `app.fontStudio.cleanupPath(${JSON.stringify(pathId)}, ${JSON.stringify(optsObj)});`
|
|
912
|
-
: `app.fontStudio.cleanupPath(${JSON.stringify(pathId)});`;
|
|
913
|
-
return executeOrGenerate(code, `Cleanup path ${pathId}`, options, 'pinepaper_font_cleanup_path');
|
|
914
|
-
}
|
|
822
|
+
// CAMERA TOOLS — extracted to src/tools/handlers/camera.ts
|
|
823
|
+
// -----------------------------------------------------------------------
|
|
824
|
+
// -----------------------------------------------------------------------
|
|
825
|
+
// FONT TOOLS — extracted to src/tools/handlers/font.ts
|
|
826
|
+
// -----------------------------------------------------------------------
|
|
915
827
|
// -----------------------------------------------------------------------
|
|
916
828
|
// QUERY TOOLS
|
|
917
829
|
// -----------------------------------------------------------------------
|
|
@@ -956,7 +868,7 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
956
868
|
case 'pinepaper_refresh_page': {
|
|
957
869
|
const controller = options.browserController || getBrowserController();
|
|
958
870
|
if (!controller.connected) {
|
|
959
|
-
return errorResult(
|
|
871
|
+
return errorResult(ErrorCodes.BROWSER_NOT_CONNECTED, 'Not connected to PinePaper Studio. Call pinepaper_browser_connect first.');
|
|
960
872
|
}
|
|
961
873
|
try {
|
|
962
874
|
// Refresh the page
|
|
@@ -985,7 +897,7 @@ You can now start creating new items on a clean canvas.`,
|
|
|
985
897
|
return { content };
|
|
986
898
|
}
|
|
987
899
|
catch (error) {
|
|
988
|
-
return errorResult(
|
|
900
|
+
return errorResult(ErrorCodes.REFRESH_FAILED, error instanceof Error ? error.message : 'Failed to refresh page');
|
|
989
901
|
}
|
|
990
902
|
}
|
|
991
903
|
// -----------------------------------------------------------------------
|
|
@@ -997,6 +909,15 @@ You can now start creating new items on a clean canvas.`,
|
|
|
997
909
|
const description = 'Imports SVG onto the canvas';
|
|
998
910
|
return executeOrGenerate(code, description, options, 'pinepaper_import_svg');
|
|
999
911
|
}
|
|
912
|
+
case 'pinepaper_import_mermaid': {
|
|
913
|
+
const input = ImportMermaidInputSchema.parse(args);
|
|
914
|
+
const code = codeGenerator.generateImportMermaid(input.mermaidText, {
|
|
915
|
+
autoLayout: input.autoLayout,
|
|
916
|
+
clearExisting: input.clearExisting,
|
|
917
|
+
});
|
|
918
|
+
const description = 'Imports a Mermaid diagram onto the canvas';
|
|
919
|
+
return executeOrGenerate(code, description, options, 'pinepaper_import_mermaid');
|
|
920
|
+
}
|
|
1000
921
|
// -----------------------------------------------------------------------
|
|
1001
922
|
// FILTER TOOLS
|
|
1002
923
|
// -----------------------------------------------------------------------
|
|
@@ -1007,46 +928,53 @@ You can now start creating new items on a clean canvas.`,
|
|
|
1007
928
|
return executeOrGenerate(code, description, options, 'pinepaper_add_filter');
|
|
1008
929
|
}
|
|
1009
930
|
// -----------------------------------------------------------------------
|
|
1010
|
-
// EXPORT TOOLS
|
|
931
|
+
// EXPORT TOOLS — extracted to src/tools/handlers/export.ts
|
|
1011
932
|
// -----------------------------------------------------------------------
|
|
1012
|
-
case 'pinepaper_export_svg': {
|
|
1013
|
-
const code = codeGenerator.generateExportSVG();
|
|
1014
|
-
const description = getLocalizedSuccessMessage(i18n, 'exported', {
|
|
1015
|
-
format: 'SVG',
|
|
1016
|
-
});
|
|
1017
|
-
return executeOrGenerate(code, description, options, 'pinepaper_export_svg');
|
|
1018
|
-
}
|
|
1019
|
-
case 'pinepaper_export_training_data': {
|
|
1020
|
-
const input = ExportTrainingDataInputSchema.parse(args);
|
|
1021
|
-
const code = codeGenerator.generateExportTrainingData(input.format, input.includeMetadata);
|
|
1022
|
-
const description = getLocalizedSuccessMessage(i18n, 'exported', {
|
|
1023
|
-
format: input.format?.toUpperCase() || 'JSON',
|
|
1024
|
-
});
|
|
1025
|
-
return executeOrGenerate(code, description, options, 'pinepaper_export_training_data');
|
|
1026
|
-
}
|
|
1027
|
-
case 'pinepaper_export_scene': {
|
|
1028
|
-
const code = codeGenerator.generateExportScene();
|
|
1029
|
-
return executeOrGenerate(code, 'Exports complete scene state', options, 'pinepaper_export_scene');
|
|
1030
|
-
}
|
|
1031
933
|
// -----------------------------------------------------------------------
|
|
1032
934
|
// BROWSER CONTROL TOOLS
|
|
1033
935
|
// -----------------------------------------------------------------------
|
|
1034
936
|
case 'pinepaper_browser_connect': {
|
|
1035
937
|
// Default to headless: false — if you explicitly call browser_connect, you likely want to see it
|
|
1036
938
|
const headless = args.headless ?? false;
|
|
1037
|
-
|
|
1038
|
-
//
|
|
1039
|
-
|
|
939
|
+
// Target a custom Studio instance (e.g. local dev at http://localhost:3000).
|
|
940
|
+
// Falls back to the PINEPAPER_STUDIO_URL env var, else PuppeteerController's
|
|
941
|
+
// built-in prod default. "/editor" + agent params are appended by the controller.
|
|
942
|
+
const rawUrl = typeof args.url === 'string' ? args.url.trim() : '';
|
|
943
|
+
const studioUrl = (rawUrl || (process.env.PINEPAPER_STUDIO_URL || '').trim()) || undefined;
|
|
944
|
+
// Only http(s) is supported — reject file://, data://, javascript:, etc.
|
|
945
|
+
if (studioUrl) {
|
|
946
|
+
const v = validateStudioUrl(studioUrl);
|
|
947
|
+
if (!v.ok)
|
|
948
|
+
return errorResult(ErrorCodes.INVALID_INPUT, v.error);
|
|
949
|
+
}
|
|
950
|
+
const cfg = studioUrl ? { headless, studioUrl } : { headless };
|
|
951
|
+
let controller = getBrowserController(cfg);
|
|
952
|
+
// NOT gated on controller.connected. getBrowserController() ignores cfg
|
|
953
|
+
// once the singleton exists, AND the singleton survives disconnect()
|
|
954
|
+
// (pinepaper_browser_disconnect doesn't null it) carrying its old
|
|
955
|
+
// studioUrl. So a stale/disconnected controller pointing at the prod
|
|
956
|
+
// default — which any prior browser_status() or connect() creates —
|
|
957
|
+
// would otherwise silently swallow an explicit url= (e.g. localhost for
|
|
958
|
+
// a dev build). Treat a different requested target as a change even when
|
|
959
|
+
// not currently connected.
|
|
960
|
+
const urlChanged = !!studioUrl &&
|
|
961
|
+
!sameStudioTarget(controller.studioUrl, studioUrl);
|
|
962
|
+
// Already connected, same headless mode, same URL → nothing to do
|
|
963
|
+
if (controller.connected && controller.isHeadless === headless && !urlChanged) {
|
|
1040
964
|
return {
|
|
1041
|
-
content: [{ type: 'text', text: `Already connected to PinePaper Studio (headless: ${headless}).` }],
|
|
965
|
+
content: [{ type: 'text', text: `Already connected to PinePaper Studio (${controller.studioUrl || 'default'}, headless: ${headless}).` }],
|
|
1042
966
|
};
|
|
1043
967
|
}
|
|
1044
|
-
//
|
|
1045
|
-
|
|
968
|
+
// Headless mode or target URL differs from the existing singleton → tear
|
|
969
|
+
// it down so the new config takes effect. Runs even when disconnected,
|
|
970
|
+
// because getBrowserController() reuses the stale singleton (and its old
|
|
971
|
+
// config) on the next call otherwise.
|
|
972
|
+
if (urlChanged || (controller.connected && controller.isHeadless !== headless)) {
|
|
1046
973
|
await resetBrowserController();
|
|
974
|
+
controller = getBrowserController(cfg);
|
|
1047
975
|
}
|
|
1048
976
|
try {
|
|
1049
|
-
const newController = controller
|
|
977
|
+
const newController = controller;
|
|
1050
978
|
if (!newController.connected)
|
|
1051
979
|
await newController.connect();
|
|
1052
980
|
const canvasSizeResult = await newController.executeCode(`const size = app.getCanvasSize ? app.getCanvasSize() : { width: 800, height: 600 }; ({ width: size.width || 800, height: size.height || 600 });`, false);
|
|
@@ -1054,7 +982,7 @@ You can now start creating new items on a clean canvas.`,
|
|
|
1054
982
|
? canvasSizeResult.result
|
|
1055
983
|
: { width: 800, height: 600 };
|
|
1056
984
|
return {
|
|
1057
|
-
content: [{ type: 'text', text: `Connected to PinePaper Studio (headless: ${headless}, canvas: ${canvasSize.width}x${canvasSize.height}).` }],
|
|
985
|
+
content: [{ type: 'text', text: `Connected to PinePaper Studio (${newController.studioUrl || 'default'}, headless: ${headless}, canvas: ${canvasSize.width}x${canvasSize.height}).` }],
|
|
1058
986
|
};
|
|
1059
987
|
}
|
|
1060
988
|
catch (error) {
|
|
@@ -1089,13 +1017,13 @@ You can now start creating new items on a clean canvas.`,
|
|
|
1089
1017
|
case 'pinepaper_browser_screenshot': {
|
|
1090
1018
|
const controller = getBrowserController();
|
|
1091
1019
|
if (!controller.connected) {
|
|
1092
|
-
return errorResult(
|
|
1020
|
+
return errorResult(ErrorCodes.BROWSER_NOT_CONNECTED, 'Not connected to PinePaper Studio. Call pinepaper_browser_connect first.');
|
|
1093
1021
|
}
|
|
1094
1022
|
const screenshot = await controller.takeScreenshot();
|
|
1095
1023
|
if (!screenshot) {
|
|
1096
1024
|
// Capture canvas state for debugging
|
|
1097
1025
|
const canvasState = await captureCanvasState(controller);
|
|
1098
|
-
return errorResult(
|
|
1026
|
+
return errorResult(ErrorCodes.SCREENSHOT_FAILED, 'Failed to capture screenshot', undefined, {
|
|
1099
1027
|
toolName: 'pinepaper_browser_screenshot',
|
|
1100
1028
|
canvasState: canvasState || undefined,
|
|
1101
1029
|
});
|
|
@@ -1160,8 +1088,9 @@ You can now start creating new items on a clean canvas.`,
|
|
|
1160
1088
|
const metricsLimit = input.metricsLimit ?? 100;
|
|
1161
1089
|
// --- Server info ---
|
|
1162
1090
|
const { detectToolkitFromEnvironment: detectToolkit, detectVerbosityFromEnvironment: detectVerbosity } = await import('./toolkits.js');
|
|
1091
|
+
const { SERVER_VERSION } = await import('../version.js');
|
|
1163
1092
|
const serverSection = {
|
|
1164
|
-
version:
|
|
1093
|
+
version: SERVER_VERSION,
|
|
1165
1094
|
toolkit: detectToolkit(),
|
|
1166
1095
|
verbosity: detectVerbosity(),
|
|
1167
1096
|
locale: process.env.PINEPAPER_LOCALE || 'en',
|
|
@@ -1382,54 +1311,8 @@ You can now start creating new items on a clean canvas.`,
|
|
|
1382
1311
|
};
|
|
1383
1312
|
}
|
|
1384
1313
|
// -----------------------------------------------------------------------
|
|
1385
|
-
// TOOL GUIDE
|
|
1314
|
+
// TOOL GUIDE — extracted to src/tools/handlers/tool-guide.ts
|
|
1386
1315
|
// -----------------------------------------------------------------------
|
|
1387
|
-
case 'pinepaper_tool_guide': {
|
|
1388
|
-
const input = ToolGuideInputSchema.parse(args);
|
|
1389
|
-
const { PINEPAPER_TOOLS, AI_AGENT_GUIDE } = await import('./definitions.js');
|
|
1390
|
-
const { TOOL_TAGS } = await import('./toolkits.js');
|
|
1391
|
-
// No args → full AI Agent Guide
|
|
1392
|
-
if (!input.tool && !input.category) {
|
|
1393
|
-
return {
|
|
1394
|
-
content: [{ type: 'text', text: AI_AGENT_GUIDE }],
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
// Specific tool → full verbose description
|
|
1398
|
-
if (input.tool) {
|
|
1399
|
-
const tool = PINEPAPER_TOOLS.find((t) => t.name === input.tool);
|
|
1400
|
-
if (!tool) {
|
|
1401
|
-
return {
|
|
1402
|
-
content: [{ type: 'text', text: `Unknown tool: ${input.tool}. Use pinepaper_tool_guide with no args to see the AI Agent Guide.` }],
|
|
1403
|
-
isError: true,
|
|
1404
|
-
};
|
|
1405
|
-
}
|
|
1406
|
-
return {
|
|
1407
|
-
content: [{ type: 'text', text: `# ${tool.name}\n\n${tool.description}` }],
|
|
1408
|
-
};
|
|
1409
|
-
}
|
|
1410
|
-
// Category → all tools in that tag with descriptions
|
|
1411
|
-
if (input.category) {
|
|
1412
|
-
const toolNames = TOOL_TAGS[input.category];
|
|
1413
|
-
if (!toolNames) {
|
|
1414
|
-
const available = Object.keys(TOOL_TAGS).join(', ');
|
|
1415
|
-
return {
|
|
1416
|
-
content: [{ type: 'text', text: `Unknown category: ${input.category}. Available: ${available}` }],
|
|
1417
|
-
isError: true,
|
|
1418
|
-
};
|
|
1419
|
-
}
|
|
1420
|
-
const lines = [`# Category: ${input.category}\n`];
|
|
1421
|
-
for (const name of toolNames) {
|
|
1422
|
-
const tool = PINEPAPER_TOOLS.find((t) => t.name === name);
|
|
1423
|
-
if (tool) {
|
|
1424
|
-
lines.push(`## ${tool.name}\n${tool.description}\n`);
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
return {
|
|
1428
|
-
content: [{ type: 'text', text: lines.join('\n') }],
|
|
1429
|
-
};
|
|
1430
|
-
}
|
|
1431
|
-
return { content: [{ type: 'text', text: AI_AGENT_GUIDE }] };
|
|
1432
|
-
}
|
|
1433
1316
|
// -----------------------------------------------------------------------
|
|
1434
1317
|
// ASSET TOOLS
|
|
1435
1318
|
// -----------------------------------------------------------------------
|
|
@@ -1713,6 +1596,37 @@ You can now start creating new items on a clean canvas.`,
|
|
|
1713
1596
|
}
|
|
1714
1597
|
case 'pinepaper_agent_batch_execute': {
|
|
1715
1598
|
const input = AgentBatchExecuteInputSchema.parse(args);
|
|
1599
|
+
// Ontology preflight — catch typos in itemType/relationType/
|
|
1600
|
+
// effectType/generatorName before generating JS, so the LLM gets a
|
|
1601
|
+
// structured hint instead of a silent failure inside the browser.
|
|
1602
|
+
// Opt-out via skipValidation: true when the caller knows what they're
|
|
1603
|
+
// doing (e.g. experimental vocabulary).
|
|
1604
|
+
if (!input.skipValidation) {
|
|
1605
|
+
const ops = input.operations;
|
|
1606
|
+
const violations = validateBatchVocabulary(ops);
|
|
1607
|
+
if (violations.length > 0) {
|
|
1608
|
+
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) => ({
|
|
1609
|
+
path: `operations[${v.opIndex}].${v.field}`,
|
|
1610
|
+
opType: v.opType,
|
|
1611
|
+
vocabulary: v.hint,
|
|
1612
|
+
})));
|
|
1613
|
+
}
|
|
1614
|
+
// Property-key typo detection — narrow scope (Levenshtein ≤2) so
|
|
1615
|
+
// legitimate extras like `gradient` pass through. Catches the
|
|
1616
|
+
// common typo class (radiues vs radius, fontSiz vs fontSize)
|
|
1617
|
+
// before browser execution silently drops them.
|
|
1618
|
+
const typos = detectBatchPropertyTypos(ops);
|
|
1619
|
+
if (typos.length > 0) {
|
|
1620
|
+
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) => ({
|
|
1621
|
+
path: `operations[${t.opIndex}].properties.${t.property}`,
|
|
1622
|
+
opType: t.opType,
|
|
1623
|
+
itemType: t.itemType,
|
|
1624
|
+
received: t.property,
|
|
1625
|
+
suggestion: t.suggestion,
|
|
1626
|
+
validKeys: t.validKeys,
|
|
1627
|
+
})));
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1716
1630
|
const code = codeGenerator.generateAgentBatchExecute(input);
|
|
1717
1631
|
const description = `Batch executed ${input.operations.length} operations${input.atomic !== false ? ' (atomic)' : ''}`;
|
|
1718
1632
|
// Check for out-of-bounds items before execution
|
|
@@ -1809,56 +1723,6 @@ You can now start creating new items on a clean canvas.`,
|
|
|
1809
1723
|
return executeOrGenerate(code, description, options, 'pinepaper_agent_analyze');
|
|
1810
1724
|
}
|
|
1811
1725
|
// -----------------------------------------------------------------------
|
|
1812
|
-
// INTERACTIVE TRIGGER TOOLS
|
|
1813
|
-
// -----------------------------------------------------------------------
|
|
1814
|
-
case 'pinepaper_add_trigger': {
|
|
1815
|
-
const input = AddTriggerInputSchema.parse(args);
|
|
1816
|
-
const code = codeGenerator.generateAddTrigger(input);
|
|
1817
|
-
const description = `Added ${input.event} trigger to ${input.itemId} with ${input.actions.length} actions`;
|
|
1818
|
-
return executeOrGenerate(code, description, options, 'pinepaper_add_trigger');
|
|
1819
|
-
}
|
|
1820
|
-
case 'pinepaper_remove_trigger': {
|
|
1821
|
-
const input = RemoveTriggerInputSchema.parse(args);
|
|
1822
|
-
const code = codeGenerator.generateRemoveTrigger(input);
|
|
1823
|
-
const description = input.removeAll
|
|
1824
|
-
? `Removed all triggers from ${input.itemId}`
|
|
1825
|
-
: `Removed ${input.event} trigger from ${input.itemId}`;
|
|
1826
|
-
return executeOrGenerate(code, description, options, 'pinepaper_remove_trigger');
|
|
1827
|
-
}
|
|
1828
|
-
case 'pinepaper_query_triggers': {
|
|
1829
|
-
const input = QueryTriggersInputSchema.parse(args);
|
|
1830
|
-
const code = codeGenerator.generateQueryTriggers(input);
|
|
1831
|
-
const description = input.itemId
|
|
1832
|
-
? `Queried triggers for ${input.itemId}`
|
|
1833
|
-
: 'Queried all triggers on canvas';
|
|
1834
|
-
return executeOrGenerate(code, description, options, 'pinepaper_query_triggers');
|
|
1835
|
-
}
|
|
1836
|
-
// -----------------------------------------------------------------------
|
|
1837
|
-
// QUIZ/LMS TOOLS
|
|
1838
|
-
// -----------------------------------------------------------------------
|
|
1839
|
-
case 'pinepaper_create_quiz': {
|
|
1840
|
-
const input = CreateQuizInputSchema.parse(args);
|
|
1841
|
-
const code = codeGenerator.generateCreateQuiz(input);
|
|
1842
|
-
const description = `Created quiz${input.title ? ` "${input.title}"` : ''} with ${input.questions.length} questions`;
|
|
1843
|
-
return executeOrGenerate(code, description, options, 'pinepaper_create_quiz');
|
|
1844
|
-
}
|
|
1845
|
-
case 'pinepaper_get_quiz_state': {
|
|
1846
|
-
const input = GetQuizStateInputSchema.parse(args);
|
|
1847
|
-
const code = codeGenerator.generateGetQuizState(input);
|
|
1848
|
-
const description = input.quizId
|
|
1849
|
-
? `Got state for quiz ${input.quizId}`
|
|
1850
|
-
: 'Got state for active quiz';
|
|
1851
|
-
return executeOrGenerate(code, description, options, 'pinepaper_get_quiz_state');
|
|
1852
|
-
}
|
|
1853
|
-
case 'pinepaper_reset_quiz': {
|
|
1854
|
-
const input = ResetQuizInputSchema.parse(args);
|
|
1855
|
-
const code = codeGenerator.generateResetQuiz(input);
|
|
1856
|
-
const description = input.quizId
|
|
1857
|
-
? `Reset quiz ${input.quizId}`
|
|
1858
|
-
: 'Reset active quiz';
|
|
1859
|
-
return executeOrGenerate(code, description, options, 'pinepaper_reset_quiz');
|
|
1860
|
-
}
|
|
1861
|
-
// -----------------------------------------------------------------------
|
|
1862
1726
|
// LETTER COLLAGE TOOLS
|
|
1863
1727
|
// -----------------------------------------------------------------------
|
|
1864
1728
|
case 'pinepaper_create_letter_collage': {
|
|
@@ -1873,154 +1737,9 @@ You can now start creating new items on a clean canvas.`,
|
|
|
1873
1737
|
const description = `Animated collage ${input.collageId} with ${input.animationType}`;
|
|
1874
1738
|
return executeOrGenerate(code, description, options, 'pinepaper_animate_letter_collage');
|
|
1875
1739
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
}
|
|
1880
|
-
// -----------------------------------------------------------------------
|
|
1881
|
-
// CANVAS PRESETS TOOLS
|
|
1882
|
-
// -----------------------------------------------------------------------
|
|
1883
|
-
case 'pinepaper_get_canvas_presets': {
|
|
1884
|
-
const code = codeGenerator.generateGetCanvasPresets();
|
|
1885
|
-
return executeOrGenerate(code, 'Gets all available canvas presets', options, 'pinepaper_get_canvas_presets');
|
|
1886
|
-
}
|
|
1887
|
-
// -----------------------------------------------------------------------
|
|
1888
|
-
// MAP TOOLS
|
|
1889
|
-
// -----------------------------------------------------------------------
|
|
1890
|
-
case 'pinepaper_load_map': {
|
|
1891
|
-
const input = LoadMapInputSchema.parse(args);
|
|
1892
|
-
const code = codeGenerator.generateLoadMap(input);
|
|
1893
|
-
const description = `Loads ${input.mapId} map with ${input.projection || 'default'} projection`;
|
|
1894
|
-
return executeOrGenerate(code, description, options, 'pinepaper_load_map');
|
|
1895
|
-
}
|
|
1896
|
-
case 'pinepaper_highlight_regions': {
|
|
1897
|
-
const input = HighlightRegionsInputSchema.parse(args);
|
|
1898
|
-
const code = codeGenerator.generateHighlightRegions(input);
|
|
1899
|
-
const description = `Highlights ${input.regionIds.length} region(s) on the map`;
|
|
1900
|
-
return executeOrGenerate(code, description, options, 'pinepaper_highlight_regions');
|
|
1901
|
-
}
|
|
1902
|
-
case 'pinepaper_unhighlight_regions': {
|
|
1903
|
-
const input = UnhighlightRegionsInputSchema.parse(args);
|
|
1904
|
-
const code = codeGenerator.generateUnhighlightRegions(input);
|
|
1905
|
-
return executeOrGenerate(code, 'Removes region highlights', options, 'pinepaper_unhighlight_regions');
|
|
1906
|
-
}
|
|
1907
|
-
case 'pinepaper_apply_data_colors': {
|
|
1908
|
-
const input = ApplyDataColorsInputSchema.parse(args);
|
|
1909
|
-
const code = codeGenerator.generateApplyDataColors(input);
|
|
1910
|
-
const regionCount = Object.keys(input.data).length;
|
|
1911
|
-
const description = `Applies choropleth coloring to ${regionCount} region(s)`;
|
|
1912
|
-
return executeOrGenerate(code, description, options, 'pinepaper_apply_data_colors');
|
|
1913
|
-
}
|
|
1914
|
-
case 'pinepaper_add_marker': {
|
|
1915
|
-
const input = AddMarkerInputSchema.parse(args);
|
|
1916
|
-
const code = codeGenerator.generateAddMarker(input);
|
|
1917
|
-
const description = `Adds marker at [${input.lat}, ${input.lon}]${input.label ? `: ${input.label}` : ''}`;
|
|
1918
|
-
return executeOrGenerate(code, description, options, 'pinepaper_add_marker');
|
|
1919
|
-
}
|
|
1920
|
-
case 'pinepaper_add_map_labels': {
|
|
1921
|
-
const input = AddMapLabelsInputSchema.parse(args);
|
|
1922
|
-
const code = codeGenerator.generateAddMapLabels(input);
|
|
1923
|
-
return executeOrGenerate(code, 'Adds labels to map regions', options, 'pinepaper_add_map_labels');
|
|
1924
|
-
}
|
|
1925
|
-
case 'pinepaper_pan_map': {
|
|
1926
|
-
const input = PanMapInputSchema.parse(args);
|
|
1927
|
-
const code = codeGenerator.generatePanMap(input);
|
|
1928
|
-
const description = `Pans map to [${input.lat}, ${input.lon}]`;
|
|
1929
|
-
return executeOrGenerate(code, description, options, 'pinepaper_pan_map');
|
|
1930
|
-
}
|
|
1931
|
-
case 'pinepaper_zoom_map': {
|
|
1932
|
-
const input = ZoomMapInputSchema.parse(args);
|
|
1933
|
-
const code = codeGenerator.generateZoomMap(input);
|
|
1934
|
-
const description = `Sets map zoom level to ${input.level}`;
|
|
1935
|
-
return executeOrGenerate(code, description, options, 'pinepaper_zoom_map');
|
|
1936
|
-
}
|
|
1937
|
-
case 'pinepaper_export_map': {
|
|
1938
|
-
const code = codeGenerator.generateExportMap();
|
|
1939
|
-
return executeOrGenerate(code, 'Exports map configuration', options, 'pinepaper_export_map');
|
|
1940
|
-
}
|
|
1941
|
-
case 'pinepaper_import_custom_map': {
|
|
1942
|
-
const input = ImportCustomMapInputSchema.parse(args);
|
|
1943
|
-
const code = codeGenerator.generateImportCustomMap(input);
|
|
1944
|
-
const description = input.url ? `Imports custom map from URL` : 'Imports custom GeoJSON map';
|
|
1945
|
-
return executeOrGenerate(code, description, options, 'pinepaper_import_custom_map');
|
|
1946
|
-
}
|
|
1947
|
-
case 'pinepaper_get_region_at_point': {
|
|
1948
|
-
const input = GetRegionAtPointInputSchema.parse(args);
|
|
1949
|
-
const code = codeGenerator.generateGetRegionAtPoint(input);
|
|
1950
|
-
const description = `Gets region at point [${input.x}, ${input.y}]`;
|
|
1951
|
-
return executeOrGenerate(code, description, options, 'pinepaper_get_region_at_point');
|
|
1952
|
-
}
|
|
1953
|
-
// -----------------------------------------------------------------------
|
|
1954
|
-
// MAP ANIMATION/CSV TOOLS
|
|
1955
|
-
// -----------------------------------------------------------------------
|
|
1956
|
-
case 'pinepaper_animate_map_regions': {
|
|
1957
|
-
const input = AnimateMapRegionsInputSchema.parse(args);
|
|
1958
|
-
const code = codeGenerator.generateAnimateMapRegions(input);
|
|
1959
|
-
const regionCount = Object.keys(input.regions).length;
|
|
1960
|
-
const description = `Animates ${regionCount} map region(s) over ${input.duration || 5}s`;
|
|
1961
|
-
return executeOrGenerate(code, description, options, 'pinepaper_animate_map_regions');
|
|
1962
|
-
}
|
|
1963
|
-
case 'pinepaper_animate_map_wave': {
|
|
1964
|
-
const input = AnimateMapWaveInputSchema.parse(args);
|
|
1965
|
-
const code = codeGenerator.generateAnimateMapWave(input);
|
|
1966
|
-
const description = `Creates ${input.waveDirection || 'horizontal'} wave animation`;
|
|
1967
|
-
return executeOrGenerate(code, description, options, 'pinepaper_animate_map_wave');
|
|
1968
|
-
}
|
|
1969
|
-
case 'pinepaper_stop_map_animations': {
|
|
1970
|
-
const input = StopMapAnimationsInputSchema.parse(args);
|
|
1971
|
-
const code = codeGenerator.generateStopMapAnimations(input);
|
|
1972
|
-
return executeOrGenerate(code, 'Stops map region animations', options, 'pinepaper_stop_map_animations');
|
|
1973
|
-
}
|
|
1974
|
-
case 'pinepaper_get_animated_map_regions': {
|
|
1975
|
-
const code = codeGenerator.generateGetAnimatedMapRegions();
|
|
1976
|
-
return executeOrGenerate(code, 'Gets list of animated map regions', options, 'pinepaper_get_animated_map_regions');
|
|
1977
|
-
}
|
|
1978
|
-
case 'pinepaper_export_map_region_csv': {
|
|
1979
|
-
const input = ExportMapRegionCSVInputSchema.parse(args);
|
|
1980
|
-
const code = codeGenerator.generateExportMapRegionCSV(input);
|
|
1981
|
-
return executeOrGenerate(code, 'Exports map region data as CSV', options, 'pinepaper_export_map_region_csv');
|
|
1982
|
-
}
|
|
1983
|
-
case 'pinepaper_import_map_region_csv': {
|
|
1984
|
-
const input = ImportMapRegionCSVInputSchema.parse(args);
|
|
1985
|
-
const code = codeGenerator.generateImportMapRegionCSV(input);
|
|
1986
|
-
return executeOrGenerate(code, 'Imports CSV data to update map regions', options, 'pinepaper_import_map_region_csv');
|
|
1987
|
-
}
|
|
1988
|
-
case 'pinepaper_select_map_regions': {
|
|
1989
|
-
const input = SelectMapRegionsInputSchema.parse(args);
|
|
1990
|
-
const code = codeGenerator.generateSelectMapRegions(input);
|
|
1991
|
-
const description = `Selects ${input.regionIds.length} map region(s)`;
|
|
1992
|
-
return executeOrGenerate(code, description, options, 'pinepaper_select_map_regions');
|
|
1993
|
-
}
|
|
1994
|
-
case 'pinepaper_deselect_map_regions': {
|
|
1995
|
-
const input = DeselectMapRegionsInputSchema.parse(args);
|
|
1996
|
-
const code = codeGenerator.generateDeselectMapRegions(input);
|
|
1997
|
-
return executeOrGenerate(code, 'Deselects map regions', options, 'pinepaper_deselect_map_regions');
|
|
1998
|
-
}
|
|
1999
|
-
case 'pinepaper_get_highlighted_map_regions': {
|
|
2000
|
-
const code = codeGenerator.generateGetHighlightedMapRegions();
|
|
2001
|
-
return executeOrGenerate(code, 'Gets list of highlighted map regions', options, 'pinepaper_get_highlighted_map_regions');
|
|
2002
|
-
}
|
|
2003
|
-
case 'pinepaper_export_map_geojson': {
|
|
2004
|
-
const code = codeGenerator.generateExportMapGeoJson({
|
|
2005
|
-
includeStyles: args.includeStyles,
|
|
2006
|
-
includeMetadata: args.includeMetadata,
|
|
2007
|
-
selectedOnly: args.selectedOnly,
|
|
2008
|
-
download: args.download,
|
|
2009
|
-
filename: args.filename,
|
|
2010
|
-
});
|
|
2011
|
-
return executeOrGenerate(code, 'Exports map as GeoJSON', options, 'pinepaper_export_map_geojson');
|
|
2012
|
-
}
|
|
2013
|
-
case 'pinepaper_export_original_map_geojson': {
|
|
2014
|
-
const code = codeGenerator.generateExportOriginalMapGeoJson({
|
|
2015
|
-
download: args.download,
|
|
2016
|
-
filename: args.filename,
|
|
2017
|
-
});
|
|
2018
|
-
return executeOrGenerate(code, 'Exports original map GeoJSON', options, 'pinepaper_export_original_map_geojson');
|
|
2019
|
-
}
|
|
2020
|
-
case 'pinepaper_get_map_source_info': {
|
|
2021
|
-
const code = codeGenerator.generateGetMapSourceInfo();
|
|
2022
|
-
return executeOrGenerate(code, 'Gets map source information', options, 'pinepaper_get_map_source_info');
|
|
2023
|
-
}
|
|
1740
|
+
// -----------------------------------------------------------------------
|
|
1741
|
+
// MAP TOOLS — extracted to src/tools/handlers/maps.ts
|
|
1742
|
+
// -----------------------------------------------------------------------
|
|
2024
1743
|
case 'pinepaper_register_item': {
|
|
2025
1744
|
const code = codeGenerator.generateRegisterItem({
|
|
2026
1745
|
itemJson: args.itemJson,
|
|
@@ -2030,7 +1749,7 @@ You can now start creating new items on a clean canvas.`,
|
|
|
2030
1749
|
return executeOrGenerate(code, 'Registers Paper.js item', options, 'pinepaper_register_item');
|
|
2031
1750
|
}
|
|
2032
1751
|
// -----------------------------------------------------------------------
|
|
2033
|
-
// TEMPLATE
|
|
1752
|
+
// TEMPLATE TOOL
|
|
2034
1753
|
// -----------------------------------------------------------------------
|
|
2035
1754
|
case 'pinepaper_apply_template': {
|
|
2036
1755
|
const parsed = ApplyTemplateInputSchema.parse(args);
|
|
@@ -2046,169 +1765,8 @@ You can now start creating new items on a clean canvas.`,
|
|
|
2046
1765
|
return executeOrGenerate(code, 'Imports image', options, 'pinepaper_import_image');
|
|
2047
1766
|
}
|
|
2048
1767
|
// -----------------------------------------------------------------------
|
|
2049
|
-
// ONTOLOGY TOOLS
|
|
1768
|
+
// ONTOLOGY TOOLS — extracted to src/tools/handlers/ontology.ts
|
|
2050
1769
|
// -----------------------------------------------------------------------
|
|
2051
|
-
case 'pinepaper_analyze_design': {
|
|
2052
|
-
const input = AnalyzeDesignInputSchema.parse(args);
|
|
2053
|
-
const { DesignGraph } = await import('../ontology/index.js');
|
|
2054
|
-
const dg = new DesignGraph();
|
|
2055
|
-
const graph = dg.extractFromDefinition(input.definition);
|
|
2056
|
-
const jsonLd = dg.toJsonLd(graph);
|
|
2057
|
-
return {
|
|
2058
|
-
content: [{
|
|
2059
|
-
type: 'text',
|
|
2060
|
-
text: JSON.stringify({
|
|
2061
|
-
templateId: graph.templateId,
|
|
2062
|
-
templateName: graph.templateName,
|
|
2063
|
-
category: graph.category,
|
|
2064
|
-
dimensions: graph.dimensions,
|
|
2065
|
-
duration: graph.duration,
|
|
2066
|
-
nodeCount: graph.nodes.length,
|
|
2067
|
-
edgeCount: graph.edges.length,
|
|
2068
|
-
nodeTypes: Object.fromEntries(Object.entries(graph.fingerprint?.nodeTypeCounts || {}).sort((a, b) => b[1] - a[1])),
|
|
2069
|
-
edgeTypes: Object.fromEntries(Object.entries(graph.fingerprint?.edgeTypeCounts || {}).sort((a, b) => b[1] - a[1])),
|
|
2070
|
-
patterns: graph.patterns,
|
|
2071
|
-
mathFunctions: graph.mathFunctions,
|
|
2072
|
-
generator: graph.generator,
|
|
2073
|
-
semantics: graph.semantics,
|
|
2074
|
-
fingerprint: graph.fingerprint,
|
|
2075
|
-
jsonLd,
|
|
2076
|
-
}, null, 2),
|
|
2077
|
-
}],
|
|
2078
|
-
};
|
|
2079
|
-
}
|
|
2080
|
-
case 'pinepaper_validate_design': {
|
|
2081
|
-
const input = ValidateDesignInputSchema.parse(args);
|
|
2082
|
-
const { DesignGraph, KnowledgeGraphValidator } = await import('../ontology/index.js');
|
|
2083
|
-
const dg = new DesignGraph();
|
|
2084
|
-
const validator = new KnowledgeGraphValidator();
|
|
2085
|
-
const definition = input.definition;
|
|
2086
|
-
const validation = validator.validateTemplate(definition);
|
|
2087
|
-
let quality = null;
|
|
2088
|
-
if (validation.valid) {
|
|
2089
|
-
const graph = dg.extractFromDefinition(definition);
|
|
2090
|
-
quality = validator.scoreQuality(definition, graph);
|
|
2091
|
-
}
|
|
2092
|
-
return {
|
|
2093
|
-
content: [{
|
|
2094
|
-
type: 'text',
|
|
2095
|
-
text: JSON.stringify({
|
|
2096
|
-
validation,
|
|
2097
|
-
quality,
|
|
2098
|
-
}, null, 2),
|
|
2099
|
-
}],
|
|
2100
|
-
};
|
|
2101
|
-
}
|
|
2102
|
-
// -----------------------------------------------------------------------
|
|
2103
|
-
// ONTOLOGY QUERY (read-only graph navigation)
|
|
2104
|
-
// -----------------------------------------------------------------------
|
|
2105
|
-
case 'pinepaper_query_ontology': {
|
|
2106
|
-
const input = QueryOntologyInputSchema.parse(args);
|
|
2107
|
-
const { PP_VOCABULARY, ITEM_TYPE_MAP, RELATION_TYPE_MAP } = await import('../ontology/vocabulary.js');
|
|
2108
|
-
const { DesignGraph } = await import('../ontology/design-graph.js');
|
|
2109
|
-
const dg = new DesignGraph();
|
|
2110
|
-
let result;
|
|
2111
|
-
switch (input.query) {
|
|
2112
|
-
case 'list_types': {
|
|
2113
|
-
const entries = Object.entries(PP_VOCABULARY.types);
|
|
2114
|
-
const filtered = entries.filter(([key, def]) => {
|
|
2115
|
-
if (input.category && def.category !== input.category)
|
|
2116
|
-
return false;
|
|
2117
|
-
if (!input.includeAbstract && def.abstract)
|
|
2118
|
-
return false;
|
|
2119
|
-
return true;
|
|
2120
|
-
});
|
|
2121
|
-
result = Object.fromEntries(filtered);
|
|
2122
|
-
break;
|
|
2123
|
-
}
|
|
2124
|
-
case 'list_edges': {
|
|
2125
|
-
const entries = Object.entries(PP_VOCABULARY.edges);
|
|
2126
|
-
const filtered = input.category
|
|
2127
|
-
? entries.filter(([, def]) => def.category === input.category)
|
|
2128
|
-
: entries;
|
|
2129
|
-
result = Object.fromEntries(filtered);
|
|
2130
|
-
break;
|
|
2131
|
-
}
|
|
2132
|
-
case 'list_generators': {
|
|
2133
|
-
const entries = Object.entries(PP_VOCABULARY.generators);
|
|
2134
|
-
const filtered = input.category
|
|
2135
|
-
? entries.filter(([, def]) => def.category === input.category)
|
|
2136
|
-
: entries;
|
|
2137
|
-
result = Object.fromEntries(filtered);
|
|
2138
|
-
break;
|
|
2139
|
-
}
|
|
2140
|
-
case 'list_effects': {
|
|
2141
|
-
result = ['sparkle', 'blast', 'smoke', 'fire', 'rain', 'snow', 'confetti', 'ripple', 'glow', 'electric'];
|
|
2142
|
-
break;
|
|
2143
|
-
}
|
|
2144
|
-
case 'list_patterns': {
|
|
2145
|
-
result = PP_VOCABULARY.patterns;
|
|
2146
|
-
break;
|
|
2147
|
-
}
|
|
2148
|
-
case 'list_math_functions': {
|
|
2149
|
-
result = PP_VOCABULARY.mathFunctions;
|
|
2150
|
-
break;
|
|
2151
|
-
}
|
|
2152
|
-
case 'type_hierarchy': {
|
|
2153
|
-
if (!input.ppType)
|
|
2154
|
-
return errorResult('INVALID_INPUT', 'ppType is required for type_hierarchy');
|
|
2155
|
-
result = dg.getTypeHierarchy(input.ppType);
|
|
2156
|
-
break;
|
|
2157
|
-
}
|
|
2158
|
-
case 'type_children': {
|
|
2159
|
-
if (!input.ppType)
|
|
2160
|
-
return errorResult('INVALID_INPUT', 'ppType is required for type_children');
|
|
2161
|
-
result = dg.getTypeChildren(input.ppType);
|
|
2162
|
-
break;
|
|
2163
|
-
}
|
|
2164
|
-
case 'type_properties': {
|
|
2165
|
-
if (!input.ppType)
|
|
2166
|
-
return errorResult('INVALID_INPUT', 'ppType is required for type_properties');
|
|
2167
|
-
result = dg.getPropertiesFor(input.ppType);
|
|
2168
|
-
break;
|
|
2169
|
-
}
|
|
2170
|
-
case 'animatable_properties': {
|
|
2171
|
-
if (!input.ppType)
|
|
2172
|
-
return errorResult('INVALID_INPUT', 'ppType is required for animatable_properties');
|
|
2173
|
-
result = dg.getAnimatableProperties(input.ppType);
|
|
2174
|
-
break;
|
|
2175
|
-
}
|
|
2176
|
-
case 'is_subtype': {
|
|
2177
|
-
if (!input.typeA || !input.typeB)
|
|
2178
|
-
return errorResult('INVALID_INPUT', 'typeA and typeB are required for is_subtype');
|
|
2179
|
-
result = { isSubtype: dg.isSubtypeOf(input.typeA, input.typeB) };
|
|
2180
|
-
break;
|
|
2181
|
-
}
|
|
2182
|
-
case 'edge_info': {
|
|
2183
|
-
const edgeName = input.relationName || input.ppType;
|
|
2184
|
-
if (!edgeName)
|
|
2185
|
-
return errorResult('INVALID_INPUT', 'relationName or ppType is required for edge_info');
|
|
2186
|
-
const ppEdge = PP_VOCABULARY.edges[edgeName] || PP_VOCABULARY.edges[RELATION_TYPE_MAP[edgeName] || ''];
|
|
2187
|
-
result = ppEdge || { error: `Unknown edge: ${edgeName}` };
|
|
2188
|
-
break;
|
|
2189
|
-
}
|
|
2190
|
-
case 'node_type': {
|
|
2191
|
-
if (!input.itemType)
|
|
2192
|
-
return errorResult('INVALID_INPUT', 'itemType is required for node_type');
|
|
2193
|
-
result = { ppType: dg.getNodeType(input.itemType) };
|
|
2194
|
-
break;
|
|
2195
|
-
}
|
|
2196
|
-
case 'edge_type': {
|
|
2197
|
-
if (!input.relationName)
|
|
2198
|
-
return errorResult('INVALID_INPUT', 'relationName is required for edge_type');
|
|
2199
|
-
result = { ppEdge: dg.getEdgeType(input.relationName) };
|
|
2200
|
-
break;
|
|
2201
|
-
}
|
|
2202
|
-
default:
|
|
2203
|
-
return errorResult('INVALID_INPUT', `Unknown query: ${input.query}`);
|
|
2204
|
-
}
|
|
2205
|
-
return {
|
|
2206
|
-
content: [{
|
|
2207
|
-
type: 'text',
|
|
2208
|
-
text: JSON.stringify(result, null, 2),
|
|
2209
|
-
}],
|
|
2210
|
-
};
|
|
2211
|
-
}
|
|
2212
1770
|
// -----------------------------------------------------------------------
|
|
2213
1771
|
// UNKNOWN TOOL
|
|
2214
1772
|
// -----------------------------------------------------------------------
|
|
@@ -2216,7 +1774,7 @@ You can now start creating new items on a clean canvas.`,
|
|
|
2216
1774
|
const message = i18n
|
|
2217
1775
|
? i18n.getError('unknownTool', { toolName })
|
|
2218
1776
|
: `Unknown tool: ${toolName}`;
|
|
2219
|
-
return errorResult(
|
|
1777
|
+
return errorResult(ErrorCodes.UNKNOWN_TOOL, message, {
|
|
2220
1778
|
availableTools: [
|
|
2221
1779
|
'pinepaper_create_item',
|
|
2222
1780
|
'pinepaper_modify_item',
|
|
@@ -2245,12 +1803,7 @@ You can now start creating new items on a clean canvas.`,
|
|
|
2245
1803
|
'pinepaper_get_mask_types',
|
|
2246
1804
|
'pinepaper_get_mask_animations',
|
|
2247
1805
|
'pinepaper_camera_animate',
|
|
2248
|
-
'
|
|
2249
|
-
'pinepaper_camera_pan',
|
|
2250
|
-
'pinepaper_camera_move_to',
|
|
2251
|
-
'pinepaper_camera_reset',
|
|
2252
|
-
'pinepaper_camera_stop',
|
|
2253
|
-
'pinepaper_camera_state',
|
|
1806
|
+
'pinepaper_camera',
|
|
2254
1807
|
'pinepaper_get_items',
|
|
2255
1808
|
'pinepaper_get_relation_stats',
|
|
2256
1809
|
'pinepaper_set_background_color',
|
|
@@ -2259,6 +1812,7 @@ You can now start creating new items on a clean canvas.`,
|
|
|
2259
1812
|
'pinepaper_clear_canvas',
|
|
2260
1813
|
'pinepaper_refresh_page',
|
|
2261
1814
|
'pinepaper_import_svg',
|
|
1815
|
+
'pinepaper_import_mermaid',
|
|
2262
1816
|
'pinepaper_add_filter',
|
|
2263
1817
|
'pinepaper_export_svg',
|
|
2264
1818
|
'pinepaper_export_training_data',
|
|
@@ -2293,64 +1847,16 @@ You can now start creating new items on a clean canvas.`,
|
|
|
2293
1847
|
'pinepaper_agent_batch_execute',
|
|
2294
1848
|
'pinepaper_agent_export',
|
|
2295
1849
|
'pinepaper_agent_analyze',
|
|
2296
|
-
// Interactive trigger tools
|
|
2297
|
-
'pinepaper_add_trigger',
|
|
2298
|
-
'pinepaper_remove_trigger',
|
|
2299
|
-
'pinepaper_query_triggers',
|
|
2300
|
-
// Quiz/LMS tools
|
|
2301
|
-
'pinepaper_create_quiz',
|
|
2302
|
-
'pinepaper_get_quiz_state',
|
|
2303
|
-
'pinepaper_reset_quiz',
|
|
2304
1850
|
// Letter collage tools
|
|
2305
1851
|
'pinepaper_create_letter_collage',
|
|
2306
1852
|
'pinepaper_animate_letter_collage',
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
'
|
|
2310
|
-
|
|
2311
|
-
'
|
|
2312
|
-
'pinepaper_highlight_regions',
|
|
2313
|
-
'pinepaper_unhighlight_regions',
|
|
2314
|
-
'pinepaper_apply_data_colors',
|
|
2315
|
-
'pinepaper_add_marker',
|
|
2316
|
-
'pinepaper_add_map_labels',
|
|
2317
|
-
'pinepaper_pan_map',
|
|
2318
|
-
'pinepaper_zoom_map',
|
|
2319
|
-
'pinepaper_export_map',
|
|
2320
|
-
'pinepaper_import_custom_map',
|
|
2321
|
-
'pinepaper_get_region_at_point',
|
|
2322
|
-
// Map animation/CSV tools
|
|
2323
|
-
'pinepaper_animate_map_regions',
|
|
2324
|
-
'pinepaper_animate_map_wave',
|
|
2325
|
-
'pinepaper_stop_map_animations',
|
|
2326
|
-
'pinepaper_get_animated_map_regions',
|
|
2327
|
-
'pinepaper_export_map_region_csv',
|
|
2328
|
-
'pinepaper_import_map_region_csv',
|
|
2329
|
-
'pinepaper_select_map_regions',
|
|
2330
|
-
'pinepaper_deselect_map_regions',
|
|
2331
|
-
'pinepaper_get_highlighted_map_regions',
|
|
2332
|
-
'pinepaper_export_map_geojson',
|
|
2333
|
-
'pinepaper_export_original_map_geojson',
|
|
2334
|
-
'pinepaper_get_map_source_info',
|
|
1853
|
+
// Map tools (4 action-dispatched)
|
|
1854
|
+
'pinepaper_map',
|
|
1855
|
+
'pinepaper_map_regions',
|
|
1856
|
+
'pinepaper_map_animation',
|
|
1857
|
+
'pinepaper_map_data',
|
|
2335
1858
|
// Font tools
|
|
2336
|
-
'
|
|
2337
|
-
'pinepaper_font_set_name',
|
|
2338
|
-
'pinepaper_font_get_required_chars',
|
|
2339
|
-
'pinepaper_font_get_status',
|
|
2340
|
-
'pinepaper_font_create_glyph',
|
|
2341
|
-
'pinepaper_font_create_space',
|
|
2342
|
-
'pinepaper_font_remove_glyph',
|
|
2343
|
-
'pinepaper_font_set_metrics',
|
|
2344
|
-
'pinepaper_font_export',
|
|
2345
|
-
'pinepaper_font_load_into_document',
|
|
2346
|
-
'pinepaper_font_export_data',
|
|
2347
|
-
'pinepaper_font_import_data',
|
|
2348
|
-
'pinepaper_font_clear',
|
|
2349
|
-
'pinepaper_font_remove_overlap',
|
|
2350
|
-
'pinepaper_font_correct_direction',
|
|
2351
|
-
'pinepaper_font_cleanup_path',
|
|
2352
|
-
// Template tools
|
|
2353
|
-
'pinepaper_apply_template',
|
|
1859
|
+
'pinepaper_font',
|
|
2354
1860
|
// Image import tools
|
|
2355
1861
|
'pinepaper_import_image',
|
|
2356
1862
|
// Paper.js direct access tools
|
|
@@ -2359,6 +1865,7 @@ You can now start creating new items on a clean canvas.`,
|
|
|
2359
1865
|
'pinepaper_tool_guide',
|
|
2360
1866
|
'pinepaper_set_toolkit',
|
|
2361
1867
|
// Ontology tools
|
|
1868
|
+
'pinepaper_get_canvas_ontology',
|
|
2362
1869
|
'pinepaper_analyze_design',
|
|
2363
1870
|
'pinepaper_validate_design',
|
|
2364
1871
|
'pinepaper_query_ontology',
|
|
@@ -2372,10 +1879,21 @@ You can now start creating new items on a clean canvas.`,
|
|
|
2372
1879
|
'pinepaper_cutout_style',
|
|
2373
1880
|
// Composition & view tools
|
|
2374
1881
|
'pinepaper_precomp',
|
|
2375
|
-
'pinepaper_view',
|
|
2376
1882
|
'pinepaper_background',
|
|
2377
1883
|
// Canvas query tools
|
|
2378
1884
|
'pinepaper_query',
|
|
1885
|
+
// Deformation, sprite sheets, interaction
|
|
1886
|
+
'pinepaper_deform',
|
|
1887
|
+
'pinepaper_sprite_sheet',
|
|
1888
|
+
'pinepaper_interaction',
|
|
1889
|
+
// Widget export
|
|
1890
|
+
'pinepaper_export_widget',
|
|
1891
|
+
'pinepaper_export_widget_html',
|
|
1892
|
+
// Data visualization, magic, physics, measurement
|
|
1893
|
+
'pinepaper_create_chart',
|
|
1894
|
+
'pinepaper_magic',
|
|
1895
|
+
'pinepaper_physics',
|
|
1896
|
+
'pinepaper_measurement',
|
|
2379
1897
|
],
|
|
2380
1898
|
});
|
|
2381
1899
|
}
|