@pinepaper.studio/mcp-server 1.5.1 → 1.5.3
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/dist/agent/types.d.ts +6 -6
- 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/index.d.ts.map +1 -1
- package/dist/index.js +80 -106
- 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/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 +395 -0
- package/dist/ontology/vocabulary.js.map +1 -0
- package/dist/tools/compact-descriptions.d.ts +2 -2
- package/dist/tools/compact-descriptions.d.ts.map +1 -1
- package/dist/tools/compact-descriptions.js +45 -233
- 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 +638 -31
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +290 -145
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +22 -2
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +31 -1
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +14 -2
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +635 -6
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +387 -82
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +136 -2
- package/dist/types/schemas.js.map +1 -1
- package/package.json +2 -2
package/dist/tools/handlers.js
CHANGED
|
@@ -30,12 +30,18 @@ ApplyTemplateInputSchema,
|
|
|
30
30
|
// Image import schemas
|
|
31
31
|
ImportImageInputSchema,
|
|
32
32
|
// Tool guide schema
|
|
33
|
-
ToolGuideInputSchema,
|
|
33
|
+
ToolGuideInputSchema,
|
|
34
|
+
// Ontology schemas
|
|
35
|
+
AnalyzeDesignInputSchema, ValidateDesignInputSchema, QueryOntologyInputSchema,
|
|
36
|
+
// Scene management schemas
|
|
37
|
+
ManageScenesInputSchema, ScenePlaybackInputSchema,
|
|
38
|
+
// New consolidated tool schemas
|
|
39
|
+
SelectionInputSchema, TransformInputSchema, HistoryInputSchema, ImageFilterInputSchema, LassoInputSchema, CutoutStyleInputSchema, PrecompInputSchema, ViewInputSchema, BackgroundInputSchema, QueryInputSchema, ErrorCodes, } from '../types/schemas.js';
|
|
34
40
|
import { ZodError } from 'zod';
|
|
35
41
|
import { writeFile, mkdir } from 'node:fs/promises';
|
|
36
42
|
import { tmpdir } from 'node:os';
|
|
37
43
|
import { join } from 'node:path';
|
|
38
|
-
import { getBrowserController, } from '../browser/puppeteer-controller.js';
|
|
44
|
+
import { getBrowserController, resetBrowserController, } from '../browser/puppeteer-controller.js';
|
|
39
45
|
import { getPerformanceTracker } from '../metrics/index.js';
|
|
40
46
|
import { formatErrorContext, captureCanvasState } from '../execution/index.js';
|
|
41
47
|
import { getSessionManager } from '../agent/session-manager.js';
|
|
@@ -52,43 +58,6 @@ export function getScreenshotMode() {
|
|
|
52
58
|
return 'on_request';
|
|
53
59
|
}
|
|
54
60
|
// =============================================================================
|
|
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
61
|
// EXPORT FILE SAVE HELPERS
|
|
93
62
|
// =============================================================================
|
|
94
63
|
function getExportDir() {
|
|
@@ -150,18 +119,13 @@ function successResult(code, description) {
|
|
|
150
119
|
return { content };
|
|
151
120
|
}
|
|
152
121
|
function executedResult(code, result, screenshot, description) {
|
|
122
|
+
const resultObj = { success: true, result };
|
|
123
|
+
if (description)
|
|
124
|
+
resultObj.description = description;
|
|
153
125
|
const content = [
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
text: `Executed PinePaper code:\n\n\`\`\`javascript\n${code}\n\`\`\`\n\nResult: ${JSON.stringify(result, null, 2)}`,
|
|
157
|
-
},
|
|
126
|
+
{ type: 'text', text: JSON.stringify(resultObj, null, 2) },
|
|
127
|
+
{ type: 'text', text: `Generated code:\n\`\`\`javascript\n${code}\n\`\`\`` },
|
|
158
128
|
];
|
|
159
|
-
if (description) {
|
|
160
|
-
content.push({
|
|
161
|
-
type: 'text',
|
|
162
|
-
text: `\n${description}`,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
129
|
if (screenshot) {
|
|
166
130
|
content.push({
|
|
167
131
|
type: 'image',
|
|
@@ -571,6 +535,78 @@ async function handleToolCallInner(toolName, args, options, tracker, baseTimerId
|
|
|
571
535
|
const description = `Creates complete scene with ${itemCount} items, ${relationCount} relations, ${animationCount} animations`;
|
|
572
536
|
return executeOrGenerate(code, description, options, 'pinepaper_create_scene');
|
|
573
537
|
}
|
|
538
|
+
case 'pinepaper_manage_scenes': {
|
|
539
|
+
const input = ManageScenesInputSchema.parse(args);
|
|
540
|
+
const code = codeGenerator.generateManageScenes(input);
|
|
541
|
+
return executeOrGenerate(code, `Scene: ${input.action}`, options, 'pinepaper_manage_scenes');
|
|
542
|
+
}
|
|
543
|
+
case 'pinepaper_scene_playback': {
|
|
544
|
+
const input = ScenePlaybackInputSchema.parse(args);
|
|
545
|
+
const code = codeGenerator.generateScenePlayback(input);
|
|
546
|
+
return executeOrGenerate(code, `Scene playback: ${input.action}`, options, 'pinepaper_scene_playback');
|
|
547
|
+
}
|
|
548
|
+
// -----------------------------------------------------------------------
|
|
549
|
+
// SELECTION, TRANSFORM & HISTORY TOOLS
|
|
550
|
+
// -----------------------------------------------------------------------
|
|
551
|
+
case 'pinepaper_selection': {
|
|
552
|
+
const input = SelectionInputSchema.parse(args);
|
|
553
|
+
const code = codeGenerator.generateSelection(input);
|
|
554
|
+
return executeOrGenerate(code, `Selection: ${input.action}`, options, 'pinepaper_selection');
|
|
555
|
+
}
|
|
556
|
+
case 'pinepaper_transform': {
|
|
557
|
+
const input = TransformInputSchema.parse(args);
|
|
558
|
+
const code = codeGenerator.generateTransform(input);
|
|
559
|
+
return executeOrGenerate(code, `Transform: ${input.action}`, options, 'pinepaper_transform');
|
|
560
|
+
}
|
|
561
|
+
case 'pinepaper_history': {
|
|
562
|
+
const input = HistoryInputSchema.parse(args);
|
|
563
|
+
const code = codeGenerator.generateHistory(input);
|
|
564
|
+
return executeOrGenerate(code, `History: ${input.action}`, options, 'pinepaper_history');
|
|
565
|
+
}
|
|
566
|
+
// -----------------------------------------------------------------------
|
|
567
|
+
// IMAGE PROCESSING TOOLS
|
|
568
|
+
// -----------------------------------------------------------------------
|
|
569
|
+
case 'pinepaper_image_filter': {
|
|
570
|
+
const input = ImageFilterInputSchema.parse(args);
|
|
571
|
+
const code = codeGenerator.generateImageFilter(input);
|
|
572
|
+
return executeOrGenerate(code, `Image filter: ${input.action}`, options, 'pinepaper_image_filter');
|
|
573
|
+
}
|
|
574
|
+
case 'pinepaper_lasso': {
|
|
575
|
+
const input = LassoInputSchema.parse(args);
|
|
576
|
+
const code = codeGenerator.generateLasso(input);
|
|
577
|
+
return executeOrGenerate(code, `Lasso: ${input.action}`, options, 'pinepaper_lasso');
|
|
578
|
+
}
|
|
579
|
+
case 'pinepaper_cutout_style': {
|
|
580
|
+
const input = CutoutStyleInputSchema.parse(args);
|
|
581
|
+
const code = codeGenerator.generateCutoutStyle(input);
|
|
582
|
+
return executeOrGenerate(code, `Cutout style: ${input.action}`, options, 'pinepaper_cutout_style');
|
|
583
|
+
}
|
|
584
|
+
// -----------------------------------------------------------------------
|
|
585
|
+
// COMPOSITION & VIEW TOOLS
|
|
586
|
+
// -----------------------------------------------------------------------
|
|
587
|
+
case 'pinepaper_precomp': {
|
|
588
|
+
const input = PrecompInputSchema.parse(args);
|
|
589
|
+
const code = codeGenerator.generatePrecomp(input);
|
|
590
|
+
return executeOrGenerate(code, `Precomp: ${input.action}`, options, 'pinepaper_precomp');
|
|
591
|
+
}
|
|
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
|
+
case 'pinepaper_background': {
|
|
598
|
+
const input = BackgroundInputSchema.parse(args);
|
|
599
|
+
const code = codeGenerator.generateBackground(input);
|
|
600
|
+
return executeOrGenerate(code, `Background: ${input.action}`, options, 'pinepaper_background');
|
|
601
|
+
}
|
|
602
|
+
// -----------------------------------------------------------------------
|
|
603
|
+
// CANVAS QUERY TOOLS
|
|
604
|
+
// -----------------------------------------------------------------------
|
|
605
|
+
case 'pinepaper_query': {
|
|
606
|
+
const input = QueryInputSchema.parse(args);
|
|
607
|
+
const code = codeGenerator.generateQuery(input);
|
|
608
|
+
return executeOrGenerate(code, `Query: ${input.action}`, options, 'pinepaper_query');
|
|
609
|
+
}
|
|
574
610
|
// -----------------------------------------------------------------------
|
|
575
611
|
// BATCH OPERATION TOOLS
|
|
576
612
|
// -----------------------------------------------------------------------
|
|
@@ -996,86 +1032,35 @@ You can now start creating new items on a clean canvas.`,
|
|
|
996
1032
|
// BROWSER CONTROL TOOLS
|
|
997
1033
|
// -----------------------------------------------------------------------
|
|
998
1034
|
case 'pinepaper_browser_connect': {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
const
|
|
1002
|
-
|
|
1003
|
-
if (controller.connected) {
|
|
1035
|
+
// Default to headless: false — if you explicitly call browser_connect, you likely want to see it
|
|
1036
|
+
const headless = args.headless ?? false;
|
|
1037
|
+
const controller = getBrowserController({ headless });
|
|
1038
|
+
// Already connected with same headless mode → nothing to do
|
|
1039
|
+
if (controller.connected && controller.isHeadless === headless) {
|
|
1004
1040
|
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
|
-
],
|
|
1041
|
+
content: [{ type: 'text', text: `Already connected to PinePaper Studio (headless: ${headless}).` }],
|
|
1011
1042
|
};
|
|
1012
1043
|
}
|
|
1044
|
+
// Already connected but different headless mode → reconnect
|
|
1045
|
+
if (controller.connected && controller.isHeadless !== headless) {
|
|
1046
|
+
await resetBrowserController();
|
|
1047
|
+
}
|
|
1013
1048
|
try {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1049
|
+
const newController = controller.connected ? controller : getBrowserController({ headless });
|
|
1050
|
+
if (!newController.connected)
|
|
1051
|
+
await newController.connect();
|
|
1052
|
+
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
1053
|
const canvasSize = canvasSizeResult.success
|
|
1018
1054
|
? canvasSizeResult.result
|
|
1019
1055
|
: { 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 };
|
|
1056
|
+
return {
|
|
1057
|
+
content: [{ type: 'text', text: `Connected to PinePaper Studio (headless: ${headless}, canvas: ${canvasSize.width}x${canvasSize.height}).` }],
|
|
1058
|
+
};
|
|
1045
1059
|
}
|
|
1046
1060
|
catch (error) {
|
|
1047
|
-
const errorMessage = error instanceof Error ? error.message : 'Failed to connect
|
|
1048
|
-
// Provide helpful fallback instructions instead of just failing
|
|
1061
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to connect';
|
|
1049
1062
|
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
|
-
],
|
|
1063
|
+
content: [{ type: 'text', text: `Browser connection failed: ${errorMessage}` }],
|
|
1079
1064
|
};
|
|
1080
1065
|
}
|
|
1081
1066
|
}
|
|
@@ -1660,33 +1645,9 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
1660
1645
|
const code = codeGenerator.generateAgentStartJob(input);
|
|
1661
1646
|
const descriptionText = `Started agent job${input.name ? ` "${input.name}"` : ''} with ${input.screenshotPolicy || 'on_complete'} screenshot policy`;
|
|
1662
1647
|
const result = await executeOrGenerate(code, descriptionText, options, 'pinepaper_agent_start_job');
|
|
1663
|
-
//
|
|
1648
|
+
// Append a 1-line workflow hint
|
|
1664
1649
|
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;
|
|
1650
|
+
result.content[0].text += `\n\nNEXT: Call batch_execute with all operations, then end_job.`;
|
|
1690
1651
|
}
|
|
1691
1652
|
return result;
|
|
1692
1653
|
}
|
|
@@ -2085,6 +2046,170 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2085
2046
|
return executeOrGenerate(code, 'Imports image', options, 'pinepaper_import_image');
|
|
2086
2047
|
}
|
|
2087
2048
|
// -----------------------------------------------------------------------
|
|
2049
|
+
// ONTOLOGY TOOLS
|
|
2050
|
+
// -----------------------------------------------------------------------
|
|
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
|
+
// -----------------------------------------------------------------------
|
|
2088
2213
|
// UNKNOWN TOOL
|
|
2089
2214
|
// -----------------------------------------------------------------------
|
|
2090
2215
|
default: {
|
|
@@ -2159,6 +2284,8 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2159
2284
|
'pinepaper_diagram_mode',
|
|
2160
2285
|
// Scene tools
|
|
2161
2286
|
'pinepaper_create_scene',
|
|
2287
|
+
'pinepaper_manage_scenes',
|
|
2288
|
+
'pinepaper_scene_playback',
|
|
2162
2289
|
// Agent flow mode tools
|
|
2163
2290
|
'pinepaper_agent_start_job',
|
|
2164
2291
|
'pinepaper_agent_end_job',
|
|
@@ -2231,6 +2358,24 @@ All PinePaper tools will work in **code-only mode**:
|
|
|
2231
2358
|
// On-demand guide & runtime config
|
|
2232
2359
|
'pinepaper_tool_guide',
|
|
2233
2360
|
'pinepaper_set_toolkit',
|
|
2361
|
+
// Ontology tools
|
|
2362
|
+
'pinepaper_analyze_design',
|
|
2363
|
+
'pinepaper_validate_design',
|
|
2364
|
+
'pinepaper_query_ontology',
|
|
2365
|
+
// Selection, transform & history tools
|
|
2366
|
+
'pinepaper_selection',
|
|
2367
|
+
'pinepaper_transform',
|
|
2368
|
+
'pinepaper_history',
|
|
2369
|
+
// Image processing tools
|
|
2370
|
+
'pinepaper_image_filter',
|
|
2371
|
+
'pinepaper_lasso',
|
|
2372
|
+
'pinepaper_cutout_style',
|
|
2373
|
+
// Composition & view tools
|
|
2374
|
+
'pinepaper_precomp',
|
|
2375
|
+
'pinepaper_view',
|
|
2376
|
+
'pinepaper_background',
|
|
2377
|
+
// Canvas query tools
|
|
2378
|
+
'pinepaper_query',
|
|
2234
2379
|
],
|
|
2235
2380
|
});
|
|
2236
2381
|
}
|