@pinepaper.studio/mcp-server 1.6.19 → 1.6.21
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.de.md +1 -1
- package/README.es.md +1 -1
- package/README.fr.md +1 -1
- package/README.hi.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +161 -5
- package/README.pt-BR.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/build-info.json +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/ontology/upstream-vocabulary.d.ts +2 -2
- package/dist/ontology/upstream-vocabulary.js +3 -3
- package/dist/ontology/upstream-vocabulary.js.map +1 -1
- package/dist/ontology/vocabulary.d.ts.map +1 -1
- package/dist/ontology/vocabulary.js +9 -1
- package/dist/ontology/vocabulary.js.map +1 -1
- package/dist/tools/choreograph.d.ts +64 -0
- package/dist/tools/choreograph.d.ts.map +1 -0
- package/dist/tools/choreograph.js +346 -0
- package/dist/tools/choreograph.js.map +1 -0
- package/dist/tools/compact-descriptions.js +1 -1
- package/dist/tools/craft-tools.d.ts +55 -0
- package/dist/tools/craft-tools.d.ts.map +1 -0
- package/dist/tools/craft-tools.js +139 -0
- package/dist/tools/craft-tools.js.map +1 -0
- package/dist/tools/definitions.d.ts +10 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +401 -32
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/effect-check.d.ts +26 -0
- package/dist/tools/effect-check.d.ts.map +1 -0
- package/dist/tools/effect-check.js +129 -0
- package/dist/tools/effect-check.js.map +1 -0
- package/dist/tools/effect-class.d.ts +31 -0
- package/dist/tools/effect-class.d.ts.map +1 -0
- package/dist/tools/effect-class.js +176 -0
- package/dist/tools/effect-class.js.map +1 -0
- package/dist/tools/engine-lists.d.ts +6 -6
- package/dist/tools/engine-lists.d.ts.map +1 -1
- package/dist/tools/engine-lists.js +11 -9
- package/dist/tools/engine-lists.js.map +1 -1
- package/dist/tools/engine-methods.d.ts.map +1 -1
- package/dist/tools/engine-methods.js +12 -0
- package/dist/tools/engine-methods.js.map +1 -1
- package/dist/tools/engine-surface.d.ts +1 -1
- package/dist/tools/engine-surface.d.ts.map +1 -1
- package/dist/tools/engine-surface.js +221 -10
- package/dist/tools/engine-surface.js.map +1 -1
- package/dist/tools/handlers/export.d.ts.map +1 -1
- package/dist/tools/handlers/export.js +8 -1
- package/dist/tools/handlers/export.js.map +1 -1
- package/dist/tools/handlers/generate.d.ts +28 -28
- package/dist/tools/handlers.d.ts +23 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +301 -6
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +16 -3
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/shape-params.d.ts +2 -2
- package/dist/tools/shape-params.d.ts.map +1 -1
- package/dist/tools/shape-params.js +25 -2
- package/dist/tools/shape-params.js.map +1 -1
- package/dist/tools/stick-vocabulary.d.ts +1 -1
- package/dist/tools/stick-vocabulary.js +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +15 -0
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +44 -1
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +1140 -128
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +3091 -584
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +308 -6
- package/dist/types/schemas.js.map +1 -1
- package/dist/vendor/design/design-style.d.ts +2 -2
- package/dist/vendor/design/design-style.d.ts.map +1 -1
- package/dist/vendor/design/design-style.js +7 -2
- package/dist/vendor/design/design-style.js.map +1 -1
- package/dist/vendor/design/subdivision-tool-schema.d.ts +16 -0
- package/dist/vendor/design/subdivision-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/subdivision-tool-schema.js +104 -0
- package/dist/vendor/design/subdivision-tool-schema.js.map +1 -0
- package/dist/vendor/design/textile-tool-schema.d.ts +14 -0
- package/dist/vendor/design/textile-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/textile-tool-schema.js +195 -0
- package/dist/vendor/design/textile-tool-schema.js.map +1 -0
- package/dist/vendor/design/ui-kit-runtime.d.ts +49 -0
- package/dist/vendor/design/ui-kit-runtime.d.ts.map +1 -0
- package/dist/vendor/design/ui-kit-runtime.js +761 -0
- package/dist/vendor/design/ui-kit-runtime.js.map +1 -0
- package/dist/vendor/design/ui-kit-tool-schema.d.ts +280 -0
- package/dist/vendor/design/ui-kit-tool-schema.d.ts.map +1 -0
- package/dist/vendor/design/ui-kit-tool-schema.js +209 -0
- package/dist/vendor/design/ui-kit-tool-schema.js.map +1 -0
- package/manifest.json +53 -1
- package/package.json +5 -4
- package/dist/assets/repositories/openclipart.d.ts +0 -41
- package/dist/assets/repositories/openclipart.d.ts.map +0 -1
- package/dist/assets/repositories/openclipart.js +0 -139
- package/dist/assets/repositories/openclipart.js.map +0 -1
- package/dist/assets/repositories/svgrepo.d.ts +0 -61
- package/dist/assets/repositories/svgrepo.d.ts.map +0 -1
- package/dist/assets/repositories/svgrepo.js +0 -276
- package/dist/assets/repositories/svgrepo.js.map +0 -1
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { COMPACT_DESCRIPTIONS } from './compact-descriptions.js';
|
|
11
11
|
import { MINIMAL_DESCRIPTIONS } from './minimal-descriptions.js';
|
|
12
|
+
import { craftToolDefinitions } from './craft-tools.js';
|
|
12
13
|
import { STICK_GAITS, STICK_POSES, STICK_SEQUENCES, STICK_EXPRESSIONS, STICK_PROPS } from './stick-vocabulary.js';
|
|
13
14
|
import { AgentBatchOperationTypeSchema, GeneratorNameSchema, ItemTypeSchema } from '../types/schemas.js';
|
|
14
15
|
import { DESIGN_MEDIA, DESIGN_MEDIA_APPLY, FILTER_TYPES, FILTER_DOCS, LINEAGE_KINDS, STYLED_SCENE_STYLES, STYLED_SCENE_STYLE_INFO } from './engine-lists.js';
|
|
@@ -27,9 +28,44 @@ const SIMPLE_ANIMATION_TYPES_LIST = SimpleAnimationTypeSchema.options.join(', ')
|
|
|
27
28
|
// (round 8 DD, 1.68) — derived now, so it cannot say what the schema refuses.
|
|
28
29
|
const KEYFRAME_EASINGS_LIST = EasingSchema.options.join(', ');
|
|
29
30
|
const EASING_LINES = KEYFRAME_EASINGS.map((e) => `- ${e}: ${EASING_DESCRIPTIONS[e]}`).join('\n');
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const DOORS = [
|
|
32
|
+
{ when: 'A scene or landscape drawn in an art style (cut paper, ink, watercolour, dither, flow, ASCII)', doors: [{ tool: 'pinepaper_styled_scene', text: 'pinepaper_styled_scene' }] },
|
|
33
|
+
{ when: 'Make THIS — an item, a character, the whole scene — look like a style (8-bit, blueprint, ink sketch, flat modern, print halftone, paper cutout…)', doors: [{ tool: 'pinepaper_look', text: 'pinepaper_look (describe first: the engine grades each look native / faithful / impression)' }] },
|
|
34
|
+
{ when: 'A story, several beats, a narrative from prose — a video or film in N parts, or a click-through widget', doors: [{ tool: 'pinepaper_story', text: "pinepaper_story (from_text; nav: 'scenes' for a VIDEO — a scene chain that cuts — 'panels' for a widget)" }, { tool: 'pinepaper_scene_playback', text: 'scene chains (pinepaper_scene_playback: cutSound, play)' }] },
|
|
35
|
+
{ when: 'An ORIGINAL character from a description (a hero with a cape, a round robot, a cat in glasses), or a cast of variants', doors: [{ tool: 'pinepaper_original_character', text: 'pinepaper_original_character (describe first, then create)' }] },
|
|
36
|
+
{ when: 'A character that acts (expressions, poses, walks)', doors: [{ tool: 'pinepaper_character', text: 'pinepaper_character' }, { tool: 'pinepaper_stick', text: 'pinepaper_stick' }, { tool: 'pinepaper_rigging', text: 'pinepaper_rigging' }, { tool: 'pinepaper_deform', text: 'squash and stretch via pinepaper_deform' }] },
|
|
37
|
+
{ when: 'A number that counts up (a KPI roll-up, a year counter, a timer)', doors: [{ tool: 'pinepaper_create_item', text: 'create_item text with counter: exact on every frame, steady width; year counters: counter {from: -30000, to: 2026, era: true}' }] },
|
|
38
|
+
{ when: 'Any item that moves with intent (pops in, hops, bounces, rolls, flies, peeks, lands)', doors: [{ tool: 'pinepaper_choreograph', text: 'pinepaper_choreograph: beats, not hand-timed keyframes' }] },
|
|
39
|
+
{ when: 'One element that becomes the next thing, and the next, with no cuts (a pill → a field → a dot → a card)', doors: [{ tool: 'pinepaper_morph_sequence', text: 'pinepaper_morph_sequence' }] },
|
|
40
|
+
{ when: 'A title or caption with craft', doors: [{ tool: 'pinepaper_text_style', text: 'pinepaper_text_style (display styles; cursive with animate writes itself on)' }, { tool: 'pinepaper_text_effect', text: 'pinepaper_text_effect' }] },
|
|
41
|
+
{ when: 'A poster or layout in an aesthetic, or a collage', doors: [{ tool: 'pinepaper_design_system', text: 'pinepaper_design_system (compose)' }, { tool: 'pinepaper_compose', text: 'a collage: pinepaper_compose' }] },
|
|
42
|
+
{ when: 'A painted, stitched, hatched, inked or cut-paper look on shapes or photos', doors: [{ tool: 'pinepaper_design_medium', text: 'pinepaper_design_medium' }, { tool: 'pinepaper_image_filter', text: 'photos also pinepaper_image_filter (watercolor, painterly)' }] },
|
|
43
|
+
{ when: 'A brand', doors: [{ tool: 'pinepaper_brand_kit', text: 'pinepaper_brand_kit set (first): every item made afterwards inherits it' }] },
|
|
44
|
+
{ when: 'Sound and music', doors: [{ tool: 'pinepaper_sound', text: 'pinepaper_sound' }, { tool: 'pinepaper_audio_beats', text: 'pinepaper_audio_beats' }, { tool: 'pinepaper_beat_cuts', text: 'pinepaper_beat_cuts' }] },
|
|
45
|
+
{ when: 'Particles that must survive save, export and the cloud (confetti bursts, a trail), or custom per-frame drawing', doors: [{ tool: 'pinepaper_emitter', text: 'pinepaper_emitter' }, { tool: 'pinepaper_render_hook', text: 'custom drawing: pinepaper_render_hook' }] },
|
|
46
|
+
{ when: 'A camera move through the piece, or 3D', doors: [{ tool: 'pinepaper_camera_director', text: 'pinepaper_camera_director' }, { tool: 'pinepaper_world3d', text: '3D: pinepaper_world3d' }, { tool: 'pinepaper_relight', text: 'pinepaper_relight' }] },
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* The door block for a host's tool list (D72; cloud request from a3). A host
|
|
50
|
+
* that offers only some tools passes their names: a row keeps only the doors
|
|
51
|
+
* that host has, and a row with none is dropped — so the model is never told
|
|
52
|
+
* to "use pinepaper_X" for a tool it cannot see. With no list, every door.
|
|
53
|
+
*/
|
|
54
|
+
export function buildDoorBlock(available) {
|
|
55
|
+
const has = available ? new Set(available) : null;
|
|
56
|
+
const rows = DOORS.map((d) => {
|
|
57
|
+
const doors = d.doors.filter((x) => !has || !x.tool || has.has(x.tool));
|
|
58
|
+
return doors.length ? `- ${d.when} → ${doors.map((x) => x.text).join('; ')}` : null;
|
|
59
|
+
}).filter(Boolean);
|
|
60
|
+
return [
|
|
61
|
+
'CHOOSE THE HIGHEST-LEVEL TOOL FIRST. Primitive shapes over a generator backdrop with a pulse is the stock result users reject; reach for it only when nothing below fits the brief.',
|
|
62
|
+
...rows,
|
|
63
|
+
'pinepaper_agent_batch_execute then does the glue in ONE call: extra items, keyframes, relations, masks, effects, playback.',
|
|
64
|
+
'If no tool reaches what the brief asks (for example, redrawing a subject in another art style), tell the user what is missing instead of handing over a lesser result as done.',
|
|
65
|
+
].join('\n');
|
|
66
|
+
}
|
|
67
|
+
/** The full door block (every tool), for the guide and a local server. */
|
|
68
|
+
export const CHOOSE_THE_DOOR = buildDoorBlock();
|
|
33
69
|
export const AI_AGENT_GUIDE = `⚠️ You are connected to PinePaper Studio via MCP tools. ALWAYS use these tools to create visual content.
|
|
34
70
|
NEVER create standalone HTML pages, React components, or web apps as a substitute. Do NOT use frontend design skills or CSS-only animations instead of PinePaper.
|
|
35
71
|
PinePaper tools ARE the implementation — they generate Paper.js/JavaScript code that runs on the canvas and produce Canvas 2D, SVG, MP4, GIF, PNG exports.
|
|
@@ -43,11 +79,14 @@ The generated code is shown in tool responses and can be copied into PinePaper's
|
|
|
43
79
|
|
|
44
80
|
⚠️ KNOWN CONFLICT: The "frontend-design" plugin instructs agents to generate HTML/CSS pages for visual content. This conflicts with PinePaper. If you keep generating HTML instead of calling PinePaper tools, the user should disable the frontend-design plugin.
|
|
45
81
|
|
|
82
|
+
${CHOOSE_THE_DOOR}
|
|
83
|
+
|
|
46
84
|
WORKFLOW — Create → Validate → Iterate:
|
|
47
85
|
|
|
48
86
|
1. pinepaper_agent_start_job (clearCanvas: true, canvasPreset)
|
|
49
|
-
2.
|
|
50
|
-
|
|
87
|
+
2. The craft tool(s) above that fit the brief, each called once.
|
|
88
|
+
Then pinepaper_agent_batch_execute — ONE call for the glue:
|
|
89
|
+
set_canvas_size → set_background → create items → keyframe_animate / animate → relation → apply_mask → apply_effect → play_timeline
|
|
51
90
|
3. pinepaper_agent_end_job — returns screenshot
|
|
52
91
|
4. Show screenshot to user for validation
|
|
53
92
|
5. If user wants changes → modify specific items or start a new job
|
|
@@ -59,13 +98,13 @@ If you restart the pipeline or re-call batch_execute, you DOUBLE every item on t
|
|
|
59
98
|
To fix mistakes: use modify/delete operations, or start a NEW job with clearCanvas: true.
|
|
60
99
|
|
|
61
100
|
RULES:
|
|
62
|
-
- NEVER restart the pipeline — one start_job, one batch_execute, one end_job
|
|
63
|
-
-
|
|
101
|
+
- NEVER restart the pipeline — one start_job, one batch_execute, one end_job; the craft tools go between them
|
|
102
|
+
- Primitive operations go in the ONE batch_execute — canvas, background, items, animation, masks, effects, playback
|
|
64
103
|
- EXCEPTION: Diagrams (flowcharts, UML) use pinepaper_create_diagram_shape + pinepaper_connect, NOT batch_execute
|
|
65
|
-
-
|
|
104
|
+
- A generator is a BACKDROP: under a composed scene it adds texture; on its own it is the stock look. Prefer a styled scene, a drawn world or a real photo for the ground of a piece
|
|
66
105
|
- Use "$0", "$1" to reference items by creation order within the batch
|
|
67
106
|
|
|
68
|
-
─── BATCH OPERATIONS (
|
|
107
|
+
─── BATCH OPERATIONS (${AgentBatchOperationTypeSchema.options.length} types, in order) ───
|
|
69
108
|
|
|
70
109
|
CANVAS: set_canvas_size (width/height or preset) → set_background (color) → execute_generator (procedural art)
|
|
71
110
|
ITEMS: create (itemType, position, properties) → modify (itemId, properties) → delete (itemId) → group (itemIds → one entity)
|
|
@@ -73,9 +112,9 @@ ANIMATE: animate (loop presets) → keyframe_animate (timed reveals) → relatio
|
|
|
73
112
|
EFFECTS: apply_mask (reveal animations) → apply_effect (sparkle/blast/bubbles/fireflies/trail...)
|
|
74
113
|
PLAY: play_timeline (start playback)
|
|
75
114
|
|
|
76
|
-
─── GENERATORS (
|
|
115
|
+
─── GENERATORS (procedural backdrops — under a scene, not as one) ───
|
|
77
116
|
|
|
78
|
-
Generators fill the canvas with procedural art
|
|
117
|
+
Generators fill the canvas with procedural art: richer than a flat colour as a backdrop, but not a composition by themselves.
|
|
79
118
|
Choose based on mood:
|
|
80
119
|
Dreamy/soft: drawBokeh, drawGradientMesh, drawOrganicFlow
|
|
81
120
|
Techy/modern: drawCircuit, drawGrid, drawWindField
|
|
@@ -105,7 +144,7 @@ Shapes: circle, rectangle, star, ellipse, triangle, polygon, line, arc, path, pe
|
|
|
105
144
|
Surfaces (render-time, evaluated per pixel — not shapes): shader, field
|
|
106
145
|
shader: a lit surface — water, liquid_metal, heatmap, gem_smoke. Set behind:true to put it UNDER the art, which is what scenery wants.
|
|
107
146
|
field: one expression, many marks — xExpr/yExpr/count, re-evaluated every frame against t.
|
|
108
|
-
Both draw
|
|
147
|
+
Both draw in the studio (live, stills, every export) and in a cloud render.
|
|
109
148
|
Text: text (content, fontSize, fontFamily, color, fontWeight)
|
|
110
149
|
Dynamic text: set contentType on text items → clock (live time), timer (elapsed), countdown (from N seconds), stopwatch (pauseable)
|
|
111
150
|
All items: opacity, shadowColor, shadowBlur, blendMode, strokeColor, strokeWidth
|
|
@@ -556,10 +595,13 @@ For glossy 3D spheres, use pinepaper_create_glossy_sphere instead. For diagonal
|
|
|
556
595
|
inputSchema: {
|
|
557
596
|
type: 'object',
|
|
558
597
|
properties: {
|
|
598
|
+
screenSpace: { type: 'boolean', description: 'HUD: true keeps the item fixed in the FRAME under camera moves; its x/y, keyframes and relations are then frame coordinates. Exports (mp4, webm, gif, png, pdf) honour it; the live preview still tilts it; SVG and Lottie ignore it (agent_export warns). A studio without it refuses.' },
|
|
599
|
+
counter: { description: "TEXT only: a number that counts on the scene clock, exact on every exported frame. {from, to, start? (s), duration? (s, 1), easing? (engine name or [x1,y1,x2,y2]), decimals?, prefix?, suffix?, locale?, grouping?, padStart?, steps?, tabular? (true: steady width)}; null removes it. A year counter: {from: -30000, to: 2026, era: true} reads '30,000 BCE' … '2026 CE' (eraNegative / eraPositive relabel; no year 0); a KPI: {from: 1.67, to: 5.58, suffix: '%'}. A studio without counters refuses.", oneOf: [{ type: 'object', additionalProperties: true }, { type: 'null' }] },
|
|
600
|
+
stepTiming: { description: "Animate 'on twos': N or {every: N, baseFps?} (every N frames of a 30 fps base unless baseFps), {fps}, or null to turn off. A stepped group steps its children; follows, spring_follow, auras, rigging and GPU generators are not stepped. A studio without it refuses.", oneOf: [{ type: 'integer', minimum: 1 }, { type: 'object', properties: { every: { type: 'integer', minimum: 1 }, baseFps: { type: 'number' }, fps: { type: 'number' } } }, { type: 'null' }] },
|
|
559
601
|
itemType: {
|
|
560
602
|
type: 'string',
|
|
561
603
|
enum: ['text', 'circle', 'star', 'rectangle', 'triangle', 'polygon', 'ellipse', 'path', 'line', 'arc', 'pentagon', 'hexagon', 'diamond', 'arrow', 'heart', 'disk', 'circle-outline', 'arrow-right', 'speech-bubble', 'speech-bubble-square', 'speech-bubble-pointed', 'thought-bubble', 'double-bubble', 'quote-bubble', 'comment-box', 'callout-box', 'shader', 'field'],
|
|
562
|
-
description: "Type of item to create. 'shader' and 'field' are RENDER-TIME SURFACES drawn per pixel
|
|
604
|
+
description: "Type of item to create. 'shader' and 'field' are RENDER-TIME SURFACES drawn per pixel, in the studio and in a cloud render. See the shader/field properties below.",
|
|
563
605
|
},
|
|
564
606
|
position: {
|
|
565
607
|
oneOf: [
|
|
@@ -586,14 +628,15 @@ For glossy 3D spheres, use pinepaper_create_glossy_sphere instead. For diagonal
|
|
|
586
628
|
|
|
587
629
|
LIFETIME — on ANY item, and the only way to build a piece that CUTS between shots:
|
|
588
630
|
- bornAt: seconds. The item does not exist before this instant.
|
|
589
|
-
- ttl: seconds it lives for. Omit for "until the end".
|
|
631
|
+
- ttl: seconds it lives for. Omit for "until the end". A numeric lifetime is
|
|
632
|
+
read as ttl, except on an emitter, where lifetime is each particle's life.
|
|
590
633
|
Without these every item is on screen for the whole render, which is what makes
|
|
591
634
|
a long piece read as one crowded frame instead of a sequence of shots.
|
|
592
635
|
They set the item's VISIBILITY for [bornAt, bornAt + ttl) — not its opacity —
|
|
593
636
|
so they compose with any keyframes, opacity fades included. Re-timing with
|
|
594
637
|
pinepaper_modify_item {bornAt, ttl} replaces the previous lifetime — pass both.
|
|
595
|
-
The result's lifetime field says whether it was applied
|
|
596
|
-
|
|
638
|
+
The result's lifetime field says whether it was applied, and persists: true
|
|
639
|
+
when it is saved with the scene; without that, re-time a reopened scene.
|
|
597
640
|
|
|
598
641
|
SHADER (itemType 'shader') — a lit surface, drawn per pixel:
|
|
599
642
|
- shader: 'water' (open sea, lit swell) | 'liquid_metal' (chrome) | 'heatmap' (thermal halo) | 'gem_smoke' (smoke) | 'electric_arc' (branching discharge, stepped flicker) | 'vortex' (hollow bright RING, sheared by differential rotation) | 'rain_veil' | 'caustics' | 'ink_bleed'
|
|
@@ -714,6 +757,9 @@ Use the collage group's id for palette/collageStyle; use a child letter's id for
|
|
|
714
757
|
inputSchema: {
|
|
715
758
|
type: 'object',
|
|
716
759
|
properties: {
|
|
760
|
+
screenSpace: { type: 'boolean', description: 'HUD: true keeps the item fixed in the FRAME under camera moves; its x/y, keyframes and relations are then frame coordinates. Exports (mp4, webm, gif, png, pdf) honour it; the live preview still tilts it; SVG and Lottie ignore it (agent_export warns). A studio without it refuses.' },
|
|
761
|
+
counter: { description: "TEXT only: a number that counts on the scene clock, exact on every exported frame. {from, to, start? (s), duration? (s, 1), easing? (engine name or [x1,y1,x2,y2]), decimals?, prefix?, suffix?, locale?, grouping?, padStart?, steps?, tabular? (true: steady width)}; null removes it. A year counter: {from: -30000, to: 2026, era: true} reads '30,000 BCE' … '2026 CE' (eraNegative / eraPositive relabel; no year 0); a KPI: {from: 1.67, to: 5.58, suffix: '%'}. A studio without counters refuses.", oneOf: [{ type: 'object', additionalProperties: true }, { type: 'null' }] },
|
|
762
|
+
stepTiming: { description: "Animate 'on twos': N or {every: N, baseFps?} (every N frames of a 30 fps base unless baseFps), {fps}, or null to turn off. A stepped group steps its children; follows, spring_follow, auras, rigging and GPU generators are not stepped. A studio without it refuses.", oneOf: [{ type: 'integer', minimum: 1 }, { type: 'object', properties: { every: { type: 'integer', minimum: 1 }, baseFps: { type: 'number' }, fps: { type: 'number' } } }, { type: 'null' }] },
|
|
717
763
|
itemId: {
|
|
718
764
|
type: 'string',
|
|
719
765
|
description: 'Registry ID of the item (e.g., "item_1")',
|
|
@@ -1073,7 +1119,7 @@ EXAMPLE (sequenceDiagram):
|
|
|
1073
1119
|
"mermaidText": "sequenceDiagram\\n Alice->>Bob: Hello\\n Bob-->>Alice: Hi back\\n Note right of Bob: Thinking..."
|
|
1074
1120
|
}
|
|
1075
1121
|
|
|
1076
|
-
Returns { success, nodeCount, edgeCount, nodes, edges, errors }.
|
|
1122
|
+
Returns { success, nodeCount, edgeCount, nodeItemIds, nodeIdMap?, edgeIds?, nodes, edges, errors }. nodeItemIds are the CANVAS ids of the shapes (item_N): pass THOSE to pinepaper_auto_layout, pinepaper_animate, pinepaper_add_relation. The mermaid ids (A, B…) in edges[].from/to name nodes in the source text and resolve to nothing on the canvas; nodeIdMap (where the studio returns it) maps one to the other, and edgeIds are the connectors' canvas ids.
|
|
1077
1123
|
|
|
1078
1124
|
NOTE: Import-only — there is no MCP equivalent of FxTool's exportMermaid. Mermaid lacks animation primitives, so a round-trip would silently drop animations.`,
|
|
1079
1125
|
inputSchema: {
|
|
@@ -1635,6 +1681,7 @@ RETURNS: create → { success, action, eventId }; pulse → { success, action, e
|
|
|
1635
1681
|
inputSchema: {
|
|
1636
1682
|
type: 'object',
|
|
1637
1683
|
properties: {
|
|
1684
|
+
at: { description: 'create: fire this event at these scene times (seconds) — a timed trigger that runs on the timeline and in video exports. The widget player does not run timed triggers.', oneOf: [{ type: 'number' }, { type: 'array', items: { type: 'number' } }] },
|
|
1638
1685
|
action: { type: 'string', enum: ['create', 'pulse'], description: "'create' a pp:event · 'pulse' it to fire" },
|
|
1639
1686
|
name: { type: 'string', description: "Event name — required for action 'create'" },
|
|
1640
1687
|
payloadType: { type: 'string', description: "Payload type label (default 'Pulse') — action 'create'" },
|
|
@@ -2355,7 +2402,7 @@ READ THIS BEFORE PROMISING A MEDIUM. Naming a medium does not create its marks.
|
|
|
2355
2402
|
Call resolve before telling a user you will render in a medium. "yes, but as an impression" is the true answer for half of them, and a flat approximation misrepresents a medium rather than approximating it.
|
|
2356
2403
|
|
|
2357
2404
|
ACTIONS:
|
|
2358
|
-
- apply: { itemId, medium, options? } — render an item in ANY medium the engine can make: ${Object.keys(DESIGN_MEDIA_APPLY).join(', ')}. One engine entry point; a medium this studio cannot apply is refused with the reason: pass it on. ink / charcoal (where the studio applies them) restyle a path, text, or group of them IN PLACE — same itemId, keyframes and relations kept — with a boiling hand-drawn line; raster and video exports boil, SVG/Lottie get the colour and width only. Their options: color, amplitude (px), wavelength (px), fps (boil rate, 12), seed, boil (default true), width (px, paths), keepColor. watercolor options: color, brush, water 0..1, opacity, edge 0..1 (the dark rim), angle, bleed, seed. oil (stylised): color, brush, length, density, toneRange, relief, angle, seed. cutPaper (vector: survives SVG/PDF/video): color, facet, jitter, shadow, shadowColor, shadowOpacity, shadowOffset [dx, dy], seed. The result replaces the source in the stacking order and hides it; applying again replaces; one undo step.
|
|
2405
|
+
- apply: { itemId, medium, options? } — on a PHOTO (an image item), watercolor and oil run as the watercolor / painterly image filters and report fidelity 'stylised' (an image filter, not paint simulated over geometry); other media are refused for photos, with the reason. On shapes — render an item in ANY medium the engine can make: ${Object.keys(DESIGN_MEDIA_APPLY).join(', ')}. One engine entry point; a medium this studio cannot apply is refused with the reason: pass it on. ink / charcoal (where the studio applies them) restyle a path, text, or group of them IN PLACE — same itemId, keyframes and relations kept — with a boiling hand-drawn line; raster and video exports boil, SVG/Lottie get the colour and width only. Their options: color, amplitude (px), wavelength (px), fps (boil rate, 12), seed, boil (default true), width (px, paths), keepColor. watercolor options: color, brush, water 0..1, opacity, edge 0..1 (the dark rim), angle, bleed, seed. oil (stylised): color, brush, length, density, toneRange, relief, angle, seed. cutPaper (vector: survives SVG/PDF/video): color, facet, jitter, shadow, shadowColor, shadowOpacity, shadowOffset [dx, dy], seed. The result replaces the source in the stacking order and hides it; applying again replaces; one undo step.
|
|
2359
2406
|
- list_media: every medium with fidelity, definition and limitation. Read it rather than assuming a fixed set — the catalogue grows (hatch and watercolor are recent additions).
|
|
2360
2407
|
- resolve: { medium } → { ok, fidelity, reason, markMaker }. ok:false with a reason is a real answer — pass it on rather than trying anyway.
|
|
2361
2408
|
- list_stitches: the thread stitches with their params.
|
|
@@ -2771,7 +2818,9 @@ The geometry is vendored from mcp-cloud, which makes this a three-repo artifact:
|
|
|
2771
2818
|
idempotentHint: false,
|
|
2772
2819
|
openWorldHint: false,
|
|
2773
2820
|
},
|
|
2774
|
-
description: `
|
|
2821
|
+
description: `FORM — nav: 'scenes' is the VIDEO story: one scene per part on a scene chain that cuts in time (export as MP4); each part's length follows its role (hook 2.5 s, stat 3 s, section 3.5 s, quote 3 s, exit 3 s; 0.4 s transitions), and the reply carries the cut times. nav: 'panels' (the default) is the WIDGET story: tabs a viewer clicks through (export_widget_html). A video brief needs 'scenes'.
|
|
2822
|
+
|
|
2823
|
+
A piece of prose becomes a scene.
|
|
2775
2824
|
|
|
2776
2825
|
- distill: reduce an article to its story beats and draw NOTHING. This is the half worth having on its own — a caller can read what the distiller made of the text, edit the beats, and only then assemble. Discovering the reading by looking at a finished scene is the expensive way round.
|
|
2777
2826
|
- from_text: distill and assemble in one call, when the reading does not need checking.
|
|
@@ -2780,6 +2829,7 @@ The geometry is vendored from mcp-cloud, which makes this a three-repo artifact:
|
|
|
2780
2829
|
inputSchema: {
|
|
2781
2830
|
type: 'object',
|
|
2782
2831
|
properties: {
|
|
2832
|
+
nav: { type: 'string', enum: ['panels', 'scenes'], description: "'scenes' = the video form (a scene chain that cuts); 'panels' = the widget form (default)" },
|
|
2783
2833
|
action: { type: 'string', enum: ['distill', 'from_text', 'apply_spec', 'plan_book'], description: 'Which story operation.' },
|
|
2784
2834
|
text: { type: 'string', description: 'distill / from_text: the prose.' },
|
|
2785
2835
|
spec: { type: 'object', description: 'apply_spec: the story spec, usually a distilled one you edited.' },
|
|
@@ -3440,6 +3490,7 @@ An event channel (pinepaper_event) is the hub: something FIRES it, and reactions
|
|
|
3440
3490
|
· on_event_fire_after (params: delay, timeline) — chain one beat to the next.
|
|
3441
3491
|
· on_event_fire_if — the only reaction that TESTS graph state rather than mutating it; params.predicate is an ExpressionIR program.
|
|
3442
3492
|
· on_event_add_relation / on_event_remove_relation — the scene rewires itself.
|
|
3493
|
+
· on_event_play_sound (source = the EVENT, target null; params: sound, gain?, note?) — play a sound when the event fires: a catalogue name (effect, drum or instrument; aliases accepted) or a define_sfx spec; gain is a multiplier capped at full scale; note is for instruments (default C5). An unknown sound is refused with the catalogue. Wire a click sound as on_click_fire (button → event) + on_event_play_sound (event → null). For a sound at a TIME, pinepaper_event create { at: seconds | [seconds] } fires the event on the timeline (and in video exports); a scene chain's cutSound puts one on every cut. LIMITS: the exported widget player does not run timed triggers, and a browser may block sound until the viewer's first click or key.
|
|
3443
3494
|
· on_event_store_set / on_event_store_increment / restores_from — persistent state. The target is a store node whose id is the storage SCOPE, so two stores in a scene and two widgets on a page do not collide. restores_from is the read half and fires ONCE at load.
|
|
3444
3495
|
- Proximity (pointer over an item): on_enter_* / on_exit_* mirror the on_event_* reactions above (set_color, set_property, set_property_from_template, set_visibility, set_data, increment, toggle) — hover states without a single line of script.
|
|
3445
3496
|
- Exclusive state: exclusive_group makes at most one member active (on_event_set_active is how you activate one — tabs, steppers, quiz cards). menubar_group is the same grouping WITHOUT the mutex.
|
|
@@ -3509,7 +3560,7 @@ Relations are COMPOSITIONAL - an item can have multiple relations that work toge
|
|
|
3509
3560
|
},
|
|
3510
3561
|
relationType: {
|
|
3511
3562
|
type: 'string',
|
|
3512
|
-
enum: ['orbits', 'follows', 'attached_to', 'maintains_distance', 'points_at', 'mirrors', 'parallax', 'bounds_to', 'anchored_in_world', 'spring_follow', 'repels', 'attracts', 'wiggle', 'animates', 'grows_from', 'staggered_with', 'indicates', 'circumscribes', 'wave_through', 'morphs_to', 'group_morphs_to', 'moves_along_path', 'construction_reveal', 'triggers_animation', 'syncs_with', 'tours', 'synced_to_audio', 'expresses', 'camera_follows', 'camera_animates', 'is_midpoint_of', 'lies_on_line', 'is_centroid_of', 'is_circumcenter_of', 'concentric_with', 'driven_by', 'time_expression', 'on_click_fire', 'on_pointer_enter_fire', 'on_pointer_exit_fire', 'on_key_fire', 'on_event_fire_after', 'on_event_fire_if', 'on_event_add_relation', 'on_event_remove_relation', 'on_event_set_color', 'on_event_set_property', 'on_event_set_property_from_template', 'on_event_set_visibility', 'on_event_set_active', 'on_event_set_data', 'on_event_increment', 'on_event_toggle', 'on_event_store_set', 'on_event_store_increment', 'on_enter_set_color', 'on_enter_set_property', 'on_enter_set_property_from_template', 'on_enter_set_visibility', 'on_enter_set_data', 'on_enter_increment', 'on_enter_toggle', 'on_exit_set_color', 'on_exit_set_property', 'on_exit_set_property_from_template', 'on_exit_set_visibility', 'on_exit_set_data', 'on_exit_increment', 'on_exit_toggle', 'exclusive_group', 'menubar_group', 'restores_from', 'on_top_of', 'below', 'beside', 'inside', 'centered_on', 'aligned_with', 'part_of', 'connects_to', 'attached_to_tail', 'head_points_to'],
|
|
3563
|
+
enum: ['orbits', 'follows', 'attached_to', 'maintains_distance', 'points_at', 'mirrors', 'parallax', 'bounds_to', 'anchored_in_world', 'spring_follow', 'repels', 'attracts', 'wiggle', 'animates', 'grows_from', 'staggered_with', 'indicates', 'circumscribes', 'wave_through', 'morphs_to', 'group_morphs_to', 'moves_along_path', 'construction_reveal', 'triggers_animation', 'syncs_with', 'tours', 'synced_to_audio', 'expresses', 'camera_follows', 'camera_animates', 'is_midpoint_of', 'lies_on_line', 'is_centroid_of', 'is_circumcenter_of', 'concentric_with', 'driven_by', 'time_expression', 'on_click_fire', 'on_pointer_enter_fire', 'on_pointer_exit_fire', 'on_key_fire', 'on_event_fire_after', 'on_event_fire_if', 'on_event_add_relation', 'on_event_remove_relation', 'on_event_set_color', 'on_event_set_property', 'on_event_set_property_from_template', 'on_event_set_visibility', 'on_event_set_active', 'on_event_set_data', 'on_event_increment', 'on_event_toggle', 'on_event_store_set', 'on_event_store_increment', 'on_event_play_sound', 'looks_at', 'reacts_to', 'drag_to_turn', 'on_enter_set_color', 'on_enter_set_property', 'on_enter_set_property_from_template', 'on_enter_set_visibility', 'on_enter_set_data', 'on_enter_increment', 'on_enter_toggle', 'on_exit_set_color', 'on_exit_set_property', 'on_exit_set_property_from_template', 'on_exit_set_visibility', 'on_exit_set_data', 'on_exit_increment', 'on_exit_toggle', 'exclusive_group', 'menubar_group', 'restores_from', 'on_top_of', 'below', 'beside', 'inside', 'centered_on', 'aligned_with', 'part_of', 'connects_to', 'attached_to_tail', 'head_points_to'],
|
|
3513
3564
|
description: 'Type of relationship. A name registered with pinepaper_register_custom_relation in this session is accepted too.',
|
|
3514
3565
|
},
|
|
3515
3566
|
params: {
|
|
@@ -3552,7 +3603,7 @@ USE WHEN:
|
|
|
3552
3603
|
targetId: { type: 'string', description: 'Target item ID' },
|
|
3553
3604
|
relationType: {
|
|
3554
3605
|
type: 'string',
|
|
3555
|
-
enum: ['orbits', 'follows', 'attached_to', 'maintains_distance', 'points_at', 'mirrors', 'parallax', 'bounds_to', 'anchored_in_world', 'spring_follow', 'repels', 'attracts', 'wiggle', 'animates', 'grows_from', 'staggered_with', 'indicates', 'circumscribes', 'wave_through', 'morphs_to', 'group_morphs_to', 'moves_along_path', 'construction_reveal', 'triggers_animation', 'syncs_with', 'tours', 'synced_to_audio', 'expresses', 'camera_follows', 'camera_animates', 'is_midpoint_of', 'lies_on_line', 'is_centroid_of', 'is_circumcenter_of', 'concentric_with', 'driven_by', 'time_expression', 'on_click_fire', 'on_pointer_enter_fire', 'on_pointer_exit_fire', 'on_key_fire', 'on_event_fire_after', 'on_event_fire_if', 'on_event_add_relation', 'on_event_remove_relation', 'on_event_set_color', 'on_event_set_property', 'on_event_set_property_from_template', 'on_event_set_visibility', 'on_event_set_active', 'on_event_set_data', 'on_event_increment', 'on_event_toggle', 'on_event_store_set', 'on_event_store_increment', 'on_enter_set_color', 'on_enter_set_property', 'on_enter_set_property_from_template', 'on_enter_set_visibility', 'on_enter_set_data', 'on_enter_increment', 'on_enter_toggle', 'on_exit_set_color', 'on_exit_set_property', 'on_exit_set_property_from_template', 'on_exit_set_visibility', 'on_exit_set_data', 'on_exit_increment', 'on_exit_toggle', 'exclusive_group', 'menubar_group', 'restores_from', 'on_top_of', 'below', 'beside', 'inside', 'centered_on', 'aligned_with', 'part_of', 'connects_to', 'attached_to_tail', 'head_points_to'],
|
|
3606
|
+
enum: ['orbits', 'follows', 'attached_to', 'maintains_distance', 'points_at', 'mirrors', 'parallax', 'bounds_to', 'anchored_in_world', 'spring_follow', 'repels', 'attracts', 'wiggle', 'animates', 'grows_from', 'staggered_with', 'indicates', 'circumscribes', 'wave_through', 'morphs_to', 'group_morphs_to', 'moves_along_path', 'construction_reveal', 'triggers_animation', 'syncs_with', 'tours', 'synced_to_audio', 'expresses', 'camera_follows', 'camera_animates', 'is_midpoint_of', 'lies_on_line', 'is_centroid_of', 'is_circumcenter_of', 'concentric_with', 'driven_by', 'time_expression', 'on_click_fire', 'on_pointer_enter_fire', 'on_pointer_exit_fire', 'on_key_fire', 'on_event_fire_after', 'on_event_fire_if', 'on_event_add_relation', 'on_event_remove_relation', 'on_event_set_color', 'on_event_set_property', 'on_event_set_property_from_template', 'on_event_set_visibility', 'on_event_set_active', 'on_event_set_data', 'on_event_increment', 'on_event_toggle', 'on_event_store_set', 'on_event_store_increment', 'on_event_play_sound', 'looks_at', 'reacts_to', 'drag_to_turn', 'on_enter_set_color', 'on_enter_set_property', 'on_enter_set_property_from_template', 'on_enter_set_visibility', 'on_enter_set_data', 'on_enter_increment', 'on_enter_toggle', 'on_exit_set_color', 'on_exit_set_property', 'on_exit_set_property_from_template', 'on_exit_set_visibility', 'on_exit_set_data', 'on_exit_increment', 'on_exit_toggle', 'exclusive_group', 'menubar_group', 'restores_from', 'on_top_of', 'below', 'beside', 'inside', 'centered_on', 'aligned_with', 'part_of', 'connects_to', 'attached_to_tail', 'head_points_to'],
|
|
3556
3607
|
description: 'Specific relation type to remove (optional - removes all if not specified)',
|
|
3557
3608
|
},
|
|
3558
3609
|
},
|
|
@@ -3580,7 +3631,7 @@ USE WHEN:
|
|
|
3580
3631
|
itemId: { type: 'string', description: 'Item to query relations for' },
|
|
3581
3632
|
relationType: {
|
|
3582
3633
|
type: 'string',
|
|
3583
|
-
enum: ['orbits', 'follows', 'attached_to', 'maintains_distance', 'points_at', 'mirrors', 'parallax', 'bounds_to', 'anchored_in_world', 'spring_follow', 'repels', 'attracts', 'wiggle', 'animates', 'grows_from', 'staggered_with', 'indicates', 'circumscribes', 'wave_through', 'morphs_to', 'group_morphs_to', 'moves_along_path', 'construction_reveal', 'triggers_animation', 'syncs_with', 'tours', 'synced_to_audio', 'expresses', 'camera_follows', 'camera_animates', 'is_midpoint_of', 'lies_on_line', 'is_centroid_of', 'is_circumcenter_of', 'concentric_with', 'driven_by', 'time_expression', 'on_click_fire', 'on_pointer_enter_fire', 'on_pointer_exit_fire', 'on_key_fire', 'on_event_fire_after', 'on_event_fire_if', 'on_event_add_relation', 'on_event_remove_relation', 'on_event_set_color', 'on_event_set_property', 'on_event_set_property_from_template', 'on_event_set_visibility', 'on_event_set_active', 'on_event_set_data', 'on_event_increment', 'on_event_toggle', 'on_event_store_set', 'on_event_store_increment', 'on_enter_set_color', 'on_enter_set_property', 'on_enter_set_property_from_template', 'on_enter_set_visibility', 'on_enter_set_data', 'on_enter_increment', 'on_enter_toggle', 'on_exit_set_color', 'on_exit_set_property', 'on_exit_set_property_from_template', 'on_exit_set_visibility', 'on_exit_set_data', 'on_exit_increment', 'on_exit_toggle', 'exclusive_group', 'menubar_group', 'restores_from', 'on_top_of', 'below', 'beside', 'inside', 'centered_on', 'aligned_with', 'part_of', 'connects_to', 'attached_to_tail', 'head_points_to'],
|
|
3634
|
+
enum: ['orbits', 'follows', 'attached_to', 'maintains_distance', 'points_at', 'mirrors', 'parallax', 'bounds_to', 'anchored_in_world', 'spring_follow', 'repels', 'attracts', 'wiggle', 'animates', 'grows_from', 'staggered_with', 'indicates', 'circumscribes', 'wave_through', 'morphs_to', 'group_morphs_to', 'moves_along_path', 'construction_reveal', 'triggers_animation', 'syncs_with', 'tours', 'synced_to_audio', 'expresses', 'camera_follows', 'camera_animates', 'is_midpoint_of', 'lies_on_line', 'is_centroid_of', 'is_circumcenter_of', 'concentric_with', 'driven_by', 'time_expression', 'on_click_fire', 'on_pointer_enter_fire', 'on_pointer_exit_fire', 'on_key_fire', 'on_event_fire_after', 'on_event_fire_if', 'on_event_add_relation', 'on_event_remove_relation', 'on_event_set_color', 'on_event_set_property', 'on_event_set_property_from_template', 'on_event_set_visibility', 'on_event_set_active', 'on_event_set_data', 'on_event_increment', 'on_event_toggle', 'on_event_store_set', 'on_event_store_increment', 'on_event_play_sound', 'looks_at', 'reacts_to', 'drag_to_turn', 'on_enter_set_color', 'on_enter_set_property', 'on_enter_set_property_from_template', 'on_enter_set_visibility', 'on_enter_set_data', 'on_enter_increment', 'on_enter_toggle', 'on_exit_set_color', 'on_exit_set_property', 'on_exit_set_property_from_template', 'on_exit_set_visibility', 'on_exit_set_data', 'on_exit_increment', 'on_exit_toggle', 'exclusive_group', 'menubar_group', 'restores_from', 'on_top_of', 'below', 'beside', 'inside', 'centered_on', 'aligned_with', 'part_of', 'connects_to', 'attached_to_tail', 'head_points_to'],
|
|
3584
3635
|
description: 'Filter by relation type (optional)',
|
|
3585
3636
|
},
|
|
3586
3637
|
direction: {
|
|
@@ -3844,6 +3895,10 @@ EASING — IT SHAPES THE SEGMENT ARRIVING AT THE KEYFRAME IT IS ON.
|
|
|
3844
3895
|
The move from keyframe A to keyframe B is eased by B's easing, not A's. Easing on the FIRST keyframe does nothing (no segment arrives at it) — a Ken Burns with easing only on its first key plays linear. Put the easing on the key you are moving TO.
|
|
3845
3896
|
${EASING_LINES}
|
|
3846
3897
|
- or a custom cubic-bezier [x1, y1, x2, y2] — x in 0..1, y may exceed 0..1 for overshoot. keyframe_animate and camera_animate only.
|
|
3898
|
+
COUNTERS: a text item with a counter (create_item / modify_item counter) also animates the numeric property counterValue, drawn through the counter's format — keyframe it to shape a count by hand (hold on a number, then race). Its keys override the counter's own from / to.
|
|
3899
|
+
|
|
3900
|
+
HOLD KEYS (where the studio has them): interpolation: 'hold' on a key holds ITS value until the next key, then cuts — no in-between. Easing 'hold' / 'step' on the ARRIVING key does the same. A studio without hold keys refuses rather than interpolate; there, two keys a frame apart make the cut. For a whole layer 'on twos', set stepTiming on the item (create_item / modify_item).
|
|
3901
|
+
|
|
3847
3902
|
Every easing renders exactly in video, GIF and APNG. Lottie and CSS get the closest single Bézier: springPlayful loses its wobble, and the in-out quart/quint/expo/circ curves are within 2–4 %. SMIL is exact for springs and those four.
|
|
3848
3903
|
|
|
3849
3904
|
CLIP WINDOW (optional — Premiere/Canva-style clip behavior):
|
|
@@ -4504,7 +4559,7 @@ SUPPORTED ANIMATIONS: bounce, breathe, fade, glow, jelly, path, pulse, rotate, s
|
|
|
4504
4559
|
target: { type: 'string', description: 'Name of target item' },
|
|
4505
4560
|
type: {
|
|
4506
4561
|
type: 'string',
|
|
4507
|
-
enum: ['orbits', 'follows', 'attached_to', 'maintains_distance', 'points_at', 'mirrors', 'parallax', 'bounds_to', 'anchored_in_world', 'spring_follow', 'repels', 'attracts', 'wiggle', 'animates', 'grows_from', 'staggered_with', 'indicates', 'circumscribes', 'wave_through', 'morphs_to', 'group_morphs_to', 'moves_along_path', 'construction_reveal', 'triggers_animation', 'syncs_with', 'tours', 'synced_to_audio', 'expresses', 'camera_follows', 'camera_animates', 'is_midpoint_of', 'lies_on_line', 'is_centroid_of', 'is_circumcenter_of', 'concentric_with', 'driven_by', 'time_expression', 'on_click_fire', 'on_pointer_enter_fire', 'on_pointer_exit_fire', 'on_key_fire', 'on_event_fire_after', 'on_event_fire_if', 'on_event_add_relation', 'on_event_remove_relation', 'on_event_set_color', 'on_event_set_property', 'on_event_set_property_from_template', 'on_event_set_visibility', 'on_event_set_active', 'on_event_set_data', 'on_event_increment', 'on_event_toggle', 'on_event_store_set', 'on_event_store_increment', 'on_enter_set_color', 'on_enter_set_property', 'on_enter_set_property_from_template', 'on_enter_set_visibility', 'on_enter_set_data', 'on_enter_increment', 'on_enter_toggle', 'on_exit_set_color', 'on_exit_set_property', 'on_exit_set_property_from_template', 'on_exit_set_visibility', 'on_exit_set_data', 'on_exit_increment', 'on_exit_toggle', 'exclusive_group', 'menubar_group', 'restores_from', 'on_top_of', 'below', 'beside', 'inside', 'centered_on', 'aligned_with', 'part_of', 'connects_to', 'attached_to_tail', 'head_points_to'],
|
|
4562
|
+
enum: ['orbits', 'follows', 'attached_to', 'maintains_distance', 'points_at', 'mirrors', 'parallax', 'bounds_to', 'anchored_in_world', 'spring_follow', 'repels', 'attracts', 'wiggle', 'animates', 'grows_from', 'staggered_with', 'indicates', 'circumscribes', 'wave_through', 'morphs_to', 'group_morphs_to', 'moves_along_path', 'construction_reveal', 'triggers_animation', 'syncs_with', 'tours', 'synced_to_audio', 'expresses', 'camera_follows', 'camera_animates', 'is_midpoint_of', 'lies_on_line', 'is_centroid_of', 'is_circumcenter_of', 'concentric_with', 'driven_by', 'time_expression', 'on_click_fire', 'on_pointer_enter_fire', 'on_pointer_exit_fire', 'on_key_fire', 'on_event_fire_after', 'on_event_fire_if', 'on_event_add_relation', 'on_event_remove_relation', 'on_event_set_color', 'on_event_set_property', 'on_event_set_property_from_template', 'on_event_set_visibility', 'on_event_set_active', 'on_event_set_data', 'on_event_increment', 'on_event_toggle', 'on_event_store_set', 'on_event_store_increment', 'on_event_play_sound', 'looks_at', 'reacts_to', 'drag_to_turn', 'on_enter_set_color', 'on_enter_set_property', 'on_enter_set_property_from_template', 'on_enter_set_visibility', 'on_enter_set_data', 'on_enter_increment', 'on_enter_toggle', 'on_exit_set_color', 'on_exit_set_property', 'on_exit_set_property_from_template', 'on_exit_set_visibility', 'on_exit_set_data', 'on_exit_increment', 'on_exit_toggle', 'exclusive_group', 'menubar_group', 'restores_from', 'on_top_of', 'below', 'beside', 'inside', 'centered_on', 'aligned_with', 'part_of', 'connects_to', 'attached_to_tail', 'head_points_to'],
|
|
4508
4563
|
},
|
|
4509
4564
|
params: {
|
|
4510
4565
|
type: 'object',
|
|
@@ -4637,6 +4692,7 @@ EXAMPLE — Slideshow:
|
|
|
4637
4692
|
inputSchema: {
|
|
4638
4693
|
type: 'object',
|
|
4639
4694
|
properties: {
|
|
4695
|
+
cutSound: { description: "create_chain: a sound at every cut — a catalogue name, or {sound, gain?, offset? (s)}. The scene_cut event also fires at each cut.", oneOf: [{ type: 'string' }, { type: 'object', additionalProperties: true }] },
|
|
4640
4696
|
action: {
|
|
4641
4697
|
type: 'string',
|
|
4642
4698
|
enum: ['create_chain', 'play', 'pause', 'resume', 'stop', 'toggle_loop', 'jump'],
|
|
@@ -5185,6 +5241,8 @@ ACTIONS:
|
|
|
5185
5241
|
- plan: Report what WOULD change (count, per-role breakdown, WCAG contrast audit) WITHOUT touching
|
|
5186
5242
|
the canvas. Call this first on an unfamiliar scene; a whole-scene recolour is hard to eyeball afterwards.
|
|
5187
5243
|
- apply: Commit it. A history snapshot is taken FIRST, so one undo puts everything back.
|
|
5244
|
+
- set: { kit } — apply it AND make it the ACTIVE brand for the rest of the piece: every shape created afterwards (create_item, batch create, agent_batch_execute) takes colors.primary, and every text colors.text with the heading font from 32 px up (body below), wherever the call names none. Pass noBrand: true in an item's properties to opt it out. Use set at the start of a multi-beat piece so beat 5 matches beat 1. It lives with the studio session: set it again after a reload.
|
|
5245
|
+
- get: the active brand (or null). clear: stop inheriting (what is on the canvas keeps its colours).
|
|
5188
5246
|
- from_url: { url, screenshots? } — read the product's REAL brand from its own site before animating anything: screenshots down the page, the logo (header image or SVG, else the site icon), and a proposed kit from the colours and fonts the site's CSS computes (buttons and links weigh most). Saved to files; the result lists them. Read in an isolated browser context: the studio is not touched. Review the kit against the screenshots, then plan / apply it. Use the brand you are given, never an invented one.
|
|
5189
5247
|
|
|
5190
5248
|
KIT SHAPE — only name and colors.primary are required:
|
|
@@ -5200,7 +5258,7 @@ Both actions return a WCAG contrast audit, so you can see failing pairs before s
|
|
|
5200
5258
|
inputSchema: {
|
|
5201
5259
|
type: 'object',
|
|
5202
5260
|
properties: {
|
|
5203
|
-
action: { type: 'string', enum: ['plan', 'apply', 'from_url'], description: 'plan (dry run), apply, or from_url (read a site into a proposed kit)' },
|
|
5261
|
+
action: { type: 'string', enum: ['plan', 'apply', 'from_url', 'set', 'get', 'clear'], description: 'plan (dry run), apply (once), set (apply + every new item inherits it), get, clear, or from_url (read a site into a proposed kit)' },
|
|
5204
5262
|
url: { type: 'string', description: "from_url: the product's site, http(s)." },
|
|
5205
5263
|
screenshots: { type: 'integer', minimum: 1, maximum: 6, description: 'from_url: screenshots down the page (default 3).' },
|
|
5206
5264
|
kit: {
|
|
@@ -5497,7 +5555,7 @@ ACTIONS:
|
|
|
5497
5555
|
idempotentHint: false,
|
|
5498
5556
|
openWorldHint: false,
|
|
5499
5557
|
},
|
|
5500
|
-
description: `Apply
|
|
5558
|
+
description: `Apply image filters to raster items: a GPU set, plus painted watercolor / painterly looks for photos.
|
|
5501
5559
|
|
|
5502
5560
|
ACTIONS:
|
|
5503
5561
|
- apply: Apply a single filter. Params: itemId, filterName, params
|
|
@@ -5510,6 +5568,8 @@ Available filters (GPU raster set):
|
|
|
5510
5568
|
- Stylize: vignette (intensity, radius), edgeDetect, halftoneDots (size, angle), halftoneCMYK (size), dither (levels), grain (amount, seed), scanlines (intensity, period), chromaticAberration (amount), blur (radius)
|
|
5511
5569
|
- Light (additive): bloom (amount, threshold, radius), glow, halation (warm-fringed bloom; color), lightShafts (amount, threshold, decay, density, x, y, color — volumetric rays from a point)
|
|
5512
5570
|
- Keying: lumaKey (threshold, softness, invert)
|
|
5571
|
+
- Pixel art: pixelate (size or cells across, levels, dither, palette [colours] — e.g. a PICO-8 set), where the studio has it
|
|
5572
|
+
- Painted (CPU, STILL images only, where the studio has them): watercolor (strength, seed, radius, edges, bleed, granulation, paper, washes — layered washes, pigment rims, paper grain), painterly (strength, seed, radius, stroke, texture, edges, vivid — brush strokes along the form). About 1 s per 1080p image; the same seed paints the same picture; values are clamped and unknown params are warned about. A video is refused (they cannot run per frame): apply to a still or a captured frame. A studio without them refuses with its list of filters.
|
|
5513
5573
|
- SECOND-INPUT (params.map = another item's id, resolved to its pixels): displace (amount, dispersion, map, mapChannel — the map's brightness pushes pixels), refract (displace with per-channel dispersion — glass), trackMatte (channel, invert, strength — prefer pinepaper_media apply_track_matte for the live version), datamosh (amount, block, map? — self-moshes without a map)
|
|
5514
5574
|
|
|
5515
5575
|
RECIPE — grunge poster: chain [grain, scanlines, vignette]. Glass header: refract with map = a gradient item.
|
|
@@ -5869,6 +5929,109 @@ PHASES: sin, blink, linear, pingpong, once, elastic, heartbeat, stepped`,
|
|
|
5869
5929
|
required: ['action'],
|
|
5870
5930
|
},
|
|
5871
5931
|
},
|
|
5932
|
+
{
|
|
5933
|
+
name: 'pinepaper_hand_drawn',
|
|
5934
|
+
annotations: {
|
|
5935
|
+
title: 'Hand-Drawn Line',
|
|
5936
|
+
readOnlyHint: false,
|
|
5937
|
+
destructiveHint: false,
|
|
5938
|
+
idempotentHint: false,
|
|
5939
|
+
openWorldHint: false,
|
|
5940
|
+
},
|
|
5941
|
+
description: `Give a path a hand-drawn look: a rough, bowed outline (sketchy), or a stroke that draws itself on (reveal).
|
|
5942
|
+
|
|
5943
|
+
USE WHEN:
|
|
5944
|
+
- "give it a hand-drawn / sketchy / rough outline"
|
|
5945
|
+
- "draw the line on like ink" (reveal)
|
|
5946
|
+
|
|
5947
|
+
STYLES:
|
|
5948
|
+
- 'sketchy' (default): one-time seeded jitter of the path's points and curve handles. Saved with the scene; exports as drawn.
|
|
5949
|
+
- 'reveal' (alias 'ink_reveal'): keyframes the item's stroke trimEnd 0 -> 1 over duration seconds, merged with any keyframes it already has. Needs itemId.
|
|
5950
|
+
- 'wobble', 'stop_motion': refused, nothing changed. A line that boils frame to frame in exports is pinepaper_design_medium medium 'ink'; the refusal returns that call, with fps as the boil rate.
|
|
5951
|
+
|
|
5952
|
+
PARAMETERS:
|
|
5953
|
+
- itemId: target item registry id (sketchy falls back to the selection)
|
|
5954
|
+
- style / mode: 'sketchy' | 'reveal' | 'ink_reveal' | 'wobble' | 'stop_motion'
|
|
5955
|
+
- roughness: point jitter in px (default 2)
|
|
5956
|
+
- bowing: curve handle jitter factor (default 1)
|
|
5957
|
+
- seed: same seed, same jitter (default 1)
|
|
5958
|
+
- duration: reveal seconds (default 2)
|
|
5959
|
+
- fps / posterizeFps: boil rate passed on in the wobble/stop_motion refusal (default 12)`,
|
|
5960
|
+
inputSchema: {
|
|
5961
|
+
type: 'object',
|
|
5962
|
+
properties: {
|
|
5963
|
+
itemId: { type: 'string', description: 'Target item registry ID (or current selection if omitted)' },
|
|
5964
|
+
style: {
|
|
5965
|
+
type: 'string',
|
|
5966
|
+
enum: ['sketchy', 'reveal', 'ink_reveal', 'wobble', 'stop_motion'],
|
|
5967
|
+
description: "Hand-drawn style (default: sketchy). wobble and stop_motion are refused with the pinepaper_design_medium call that does it",
|
|
5968
|
+
},
|
|
5969
|
+
mode: {
|
|
5970
|
+
type: 'string',
|
|
5971
|
+
enum: ['sketchy', 'reveal', 'ink_reveal', 'wobble', 'stop_motion'],
|
|
5972
|
+
description: 'Alias for style',
|
|
5973
|
+
},
|
|
5974
|
+
roughness: { type: 'number', description: 'Noise magnitude in pixels (default: 2)' },
|
|
5975
|
+
bowing: { type: 'number', description: 'Line bowing curve handle factor (default: 1)' },
|
|
5976
|
+
fps: { type: 'number', description: 'wobble/stop_motion: the boil rate handed to pinepaper_design_medium (default: 12)' },
|
|
5977
|
+
posterizeFps: { type: 'number', description: 'Alias for fps' },
|
|
5978
|
+
seed: { type: 'number', description: 'Random seed for deterministic vertex displacement (default: 1)' },
|
|
5979
|
+
duration: { type: 'number', description: 'reveal: seconds to draw the stroke on (default: 2)' },
|
|
5980
|
+
},
|
|
5981
|
+
},
|
|
5982
|
+
},
|
|
5983
|
+
{
|
|
5984
|
+
name: 'pinepaper_pen_figure',
|
|
5985
|
+
annotations: {
|
|
5986
|
+
title: 'Pen Figure',
|
|
5987
|
+
readOnlyHint: false,
|
|
5988
|
+
destructiveHint: false,
|
|
5989
|
+
idempotentHint: false,
|
|
5990
|
+
openWorldHint: false,
|
|
5991
|
+
},
|
|
5992
|
+
description: `Draw a mathematical figure with a pen: the tip travels the figure and the ink appears behind it, in the editor and in every export.
|
|
5993
|
+
|
|
5994
|
+
USE WHEN:
|
|
5995
|
+
- "draw a Lorenz attractor / Lissajous / Hilbert curve being drawn"
|
|
5996
|
+
- "a pen plotting a curve", "a harmonograph drawing itself"
|
|
5997
|
+
|
|
5998
|
+
The figures are the studio's catalogue of declarative maths (parametric and polar curves, ODE attractors, iterated maps, L-systems). action 'list' returns it: id, title, kind. A figure is fitted into its box by one uniform scale, so it is the same figure on any canvas size.
|
|
5999
|
+
|
|
6000
|
+
ACTIONS:
|
|
6001
|
+
- create (default): { figure, box?, margin?, duration?, delay?, loop?, pen?, strokeColor?, penColor?, strokeWidth? } → { inkId, penId, title, strokes }. The ink is a group, one child per pen stroke; the pen is a circle joined to it by the draws_figure relation. pen: false draws the ink complete, with no pen and no animation. An unknown figure is refused with the catalogue's ids.
|
|
6002
|
+
- list: the catalogue.
|
|
6003
|
+
|
|
6004
|
+
To draw a static curve from your own equation, use pinepaper_equation_path.
|
|
6005
|
+
|
|
6006
|
+
EXAMPLES:
|
|
6007
|
+
- { figure: 'lorenz', duration: 8 }
|
|
6008
|
+
- { figure: 'hilbert', box: { x: 100, y: 100, width: 600, height: 600 }, strokeColor: '#0f766e' }
|
|
6009
|
+
- { figure: 'harmonograph', pen: false }`,
|
|
6010
|
+
inputSchema: {
|
|
6011
|
+
type: 'object',
|
|
6012
|
+
properties: {
|
|
6013
|
+
action: { type: 'string', enum: ['create', 'list'], description: "'create' (default) draws a figure; 'list' returns the catalogue." },
|
|
6014
|
+
figure: {
|
|
6015
|
+
anyOf: [{ type: 'string' }, { type: 'object' }],
|
|
6016
|
+
description: "create: a catalogue id from action 'list' (e.g. 'lorenz', 'lissajous', 'hilbert', 'harmonograph'), or an inline figure spec object.",
|
|
6017
|
+
},
|
|
6018
|
+
box: {
|
|
6019
|
+
type: 'object',
|
|
6020
|
+
properties: { x: { type: 'number' }, y: { type: 'number' }, width: { type: 'number' }, height: { type: 'number' } },
|
|
6021
|
+
required: ['x', 'y', 'width', 'height'],
|
|
6022
|
+
description: 'Where to draw, in canvas px (default: the whole canvas).',
|
|
6023
|
+
},
|
|
6024
|
+
margin: { type: 'number', description: 'Fraction of the box left clear (default 0.06).' },
|
|
6025
|
+
duration: { type: 'number', description: 'Seconds the pen takes for the whole figure, pen-up travel included (default 6).' },
|
|
6026
|
+
delay: { type: 'number', description: 'Seconds before the pen starts (default 0).' },
|
|
6027
|
+
loop: { type: 'boolean', description: 'Draw the figure again after it finishes (default false: it stays drawn).' },
|
|
6028
|
+
pen: { type: 'boolean', description: 'false: the ink only, fully drawn, no animation (default true).' },
|
|
6029
|
+
strokeColor: { type: 'string', description: "Ink colour (default '#1e293b')." },
|
|
6030
|
+
penColor: { type: 'string', description: "Pen tip colour (default '#e11d48')." },
|
|
6031
|
+
strokeWidth: { type: 'number', description: 'Ink width in px (default 2).' },
|
|
6032
|
+
},
|
|
6033
|
+
},
|
|
6034
|
+
},
|
|
5872
6035
|
{
|
|
5873
6036
|
name: 'pinepaper_sprite_sheet',
|
|
5874
6037
|
annotations: {
|
|
@@ -6506,7 +6669,7 @@ EXAMPLES:
|
|
|
6506
6669
|
- Grid of icons: {layoutType: "grid", options: {columns: 4, cellWidth: 120}}
|
|
6507
6670
|
|
|
6508
6671
|
PARTIAL LAYOUT:
|
|
6509
|
-
Pass itemIds array to only layout specific items, leaving others in place.`,
|
|
6672
|
+
Pass itemIds array to only layout specific items, leaving others in place. They are CANVAS ids (item_N) — after pinepaper_import_mermaid, use its nodeItemIds, not the mermaid ids (A, B…), which lay out nothing.`,
|
|
6510
6673
|
inputSchema: {
|
|
6511
6674
|
type: 'object',
|
|
6512
6675
|
properties: {
|
|
@@ -6518,7 +6681,7 @@ Pass itemIds array to only layout specific items, leaving others in place.`,
|
|
|
6518
6681
|
itemIds: {
|
|
6519
6682
|
type: 'array',
|
|
6520
6683
|
items: { type: 'string' },
|
|
6521
|
-
description: '
|
|
6684
|
+
description: 'Canvas item ids (item_N) to layout (default: all diagram items). After import_mermaid, its nodeItemIds.',
|
|
6522
6685
|
},
|
|
6523
6686
|
options: {
|
|
6524
6687
|
type: 'object',
|
|
@@ -7396,6 +7559,8 @@ USE WHEN:
|
|
|
7396
7559
|
},
|
|
7397
7560
|
description: `Apply a visual effect to an item.
|
|
7398
7561
|
|
|
7562
|
+
EFFECT OR EMITTER: apply_effect is a live look on an item. For particles a saved scene, a template, the cloud render and every export must reproduce frame for frame — confetti at 2 s, a trail behind a moving item — use pinepaper_emitter: its particles are a pure function of its spec and the scene time.
|
|
7563
|
+
|
|
7399
7564
|
USE WHEN:
|
|
7400
7565
|
- Adding sparkle/glitter effects
|
|
7401
7566
|
- Creating burst/explosion, fire, smoke effects
|
|
@@ -7708,6 +7873,205 @@ EXAMPLE: { style: 'ink', duration: 6 }`,
|
|
|
7708
7873
|
},
|
|
7709
7874
|
},
|
|
7710
7875
|
},
|
|
7876
|
+
{
|
|
7877
|
+
name: 'pinepaper_look',
|
|
7878
|
+
annotations: { title: 'Look', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
7879
|
+
description: `Restyle what is already drawn — one item, a group and everything in it, or the WHOLE SCENE — as a named look: palette remapped in place (colour keyframes too), strokes, type, image treatment, texture, background and a framing device. Ids, keyframes and relations are kept; it survives a project round trip; remove restores it pixel for pixel. This is "the same subject, in style X".
|
|
7880
|
+
|
|
7881
|
+
ACTIONS:
|
|
7882
|
+
- list: every look with its FIDELITY — the engine's own grade: native (the medium itself), faithful (close, its approximations named), impression (an evocation, not the real style).
|
|
7883
|
+
- describe: { look } — the recipe, its fidelity, what it approximates, and the engine call per part. Read it before promising a look.
|
|
7884
|
+
- apply: { look, target? ('scene' default | an itemId), scope? ('item' | 'subtree' | 'scene') } → what it restyled, and the fidelity. An impression comes with a warning — present it as an approximation, not the finished style; a faithful look lists what it approximates.
|
|
7885
|
+
- remove: { target } — restore what was there. get: { target } — the look on it now.
|
|
7886
|
+
- grid: { looks?, cols?, cellWidth?, t? } — a contact sheet of the scene in several looks, to choose from: the PNG is the reply's \`data\` (a data URL, as agent_export returns it), with cells [{ id, name, fidelity }].
|
|
7887
|
+
|
|
7888
|
+
look is an id from list, or a look spec. An unknown look is refused with the known ones.
|
|
7889
|
+
Looks act on what is THERE: they restyle a drawing, they do not redraw a subject from scratch in another medium.`,
|
|
7890
|
+
inputSchema: {
|
|
7891
|
+
type: 'object',
|
|
7892
|
+
properties: {
|
|
7893
|
+
action: { type: 'string', enum: ['list', 'describe', 'apply', 'remove', 'get', 'grid'] },
|
|
7894
|
+
look: { description: 'a look id, or a look spec', oneOf: [{ type: 'string' }, { type: 'object', additionalProperties: true }] },
|
|
7895
|
+
target: { type: 'string', description: "an itemId, or 'scene' (default)" },
|
|
7896
|
+
scope: { type: 'string', enum: ['item', 'subtree', 'scene'] },
|
|
7897
|
+
looks: { type: 'array', items: { type: 'string' } },
|
|
7898
|
+
cols: { type: 'integer' }, cellWidth: { type: 'number' }, t: { type: 'number' },
|
|
7899
|
+
},
|
|
7900
|
+
required: ['action'],
|
|
7901
|
+
},
|
|
7902
|
+
},
|
|
7903
|
+
{
|
|
7904
|
+
name: 'pinepaper_original_character',
|
|
7905
|
+
annotations: { title: 'Original Character', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
7906
|
+
description: `Make an ORIGINAL character from a base rig and a declarative variant — no drawing code, and nothing anyone else owns. Every variant keeps its base's parts and rig, so expressions, walks, poses and pinepaper_choreograph (which drives its root) all work on it.
|
|
7907
|
+
|
|
7908
|
+
ACTIONS:
|
|
7909
|
+
- bases: the bases (person, robot, cat, bird, blob) with their roles.
|
|
7910
|
+
- describe: { base? } — every legal palette key, feature value, accessory (and its slot), proportion range and look. READ IT FIRST: a value it does not list is refused by name and nothing is drawn.
|
|
7911
|
+
- random: { base, seed, fixed? } — a distinct, valid variant; the same seed gives the same one. fixed is shared by every member (a look, proportions) while each draws its own signature — a cast of 12 is twelve seeds with one fixed.
|
|
7912
|
+
- create: { base, variant?: { palette, features, look, seed, name }, at: { x, y }, height? (320) | scale?, rig? (true) } → itemId (the root), rigId, parts { role: id }. y is the FEET.
|
|
7913
|
+
|
|
7914
|
+
FEATURES: hair, eyes, eyeColor, nose, mouth, ears, brows, accessories (12 slots, one per slot), proportions { headScale, bodyScale, limbLength }.
|
|
7915
|
+
LOOKS on a rig: flat, ink, charcoal, paper-cutout, pixel. watercolor / oil / hatch are refused with the reason.
|
|
7916
|
+
|
|
7917
|
+
INTERACTION (pinepaper_add_relation, source = the character's itemId):
|
|
7918
|
+
- looks_at → an item id, or target 'cursor': the face follows it.
|
|
7919
|
+
- reacts_to → an event or another character; params reaction (hop | jolt | shake | nod | an expression), after, duration, amount.
|
|
7920
|
+
- drag_to_turn (no target): dragging turns the head; params max, sensitivity, turn (keyframeable), spring.
|
|
7921
|
+
- a sound on the reaction: on_event_play_sound on the same event.
|
|
7922
|
+
|
|
7923
|
+
EXAMPLE: { action: 'create', base: 'person', at: { x: 480, y: 900 }, variant: { palette: { skin: '#e8b48a', hair: '#2b1d16', outfit: '#3a7bd5', accent: '#f2c14e' }, features: { hair: 'bun', eyes: 'almond', mouth: 'smile', accessories: ['cape'] }, look: 'ink', name: 'Mira' } }`,
|
|
7924
|
+
inputSchema: {
|
|
7925
|
+
type: 'object',
|
|
7926
|
+
properties: {
|
|
7927
|
+
action: { type: 'string', enum: ['bases', 'describe', 'random', 'create'] },
|
|
7928
|
+
base: { type: 'string' }, variant: { type: 'object', additionalProperties: true },
|
|
7929
|
+
seed: { type: 'integer' }, fixed: { type: 'object', additionalProperties: true },
|
|
7930
|
+
at: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' } } },
|
|
7931
|
+
height: { type: 'number' }, scale: { type: 'number' }, rig: { type: 'boolean' },
|
|
7932
|
+
},
|
|
7933
|
+
required: ['action'],
|
|
7934
|
+
},
|
|
7935
|
+
},
|
|
7936
|
+
{
|
|
7937
|
+
name: 'pinepaper_morph_sequence',
|
|
7938
|
+
annotations: { title: 'Morph Sequence', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
7939
|
+
description: `ONE element through many states, with no cuts: each state grows out of the last (a pill becomes an input field becomes a dot becomes a card). The one-take UI-launch idiom, from one call.
|
|
7940
|
+
|
|
7941
|
+
{ itemId, states: [{ at, duration? (0.8), to: itemId | shape: { itemType, position, properties? } }], easing?, camera?, sound? }
|
|
7942
|
+
- Each state is the shape the element morphs INTO, starting at \`at\` seconds. Name an existing item (to) or describe one (shape); either way the reference is hidden and the element keeps its own id throughout. States run one after another; an overlap is refused.
|
|
7943
|
+
- easing: linear | easeIn | easeOut | easeInOut (default easeInOut) — the four the morph supports.
|
|
7944
|
+
- camera: true (or { fill, easing }) keys the camera on the SAME timing: it holds while a state rests and moves with each morph, framing the new state to \`fill\` of the frame (default 0.45).
|
|
7945
|
+
- sound: true puts a whoosh at each morph start (or name any catalogue sound).
|
|
7946
|
+
|
|
7947
|
+
Shapes morph by kind: path → path point by point, circle → circle by radius, text → text by font, across kinds by a particle transition. Colours and size morph too.
|
|
7948
|
+
|
|
7949
|
+
RETURNS: states [{ at, duration, targetId }], end (seconds), camera { keyframes }, sounds.
|
|
7950
|
+
|
|
7951
|
+
EXAMPLE — pill → field → dot:
|
|
7952
|
+
{ itemId: "item_1", camera: true, sound: true, states: [
|
|
7953
|
+
{ at: 1, shape: { itemType: "rectangle", position: { x: 960, y: 540 }, properties: { width: 640, height: 88, cornerRadius: 44, color: "#ffffff" } } },
|
|
7954
|
+
{ at: 3, shape: { itemType: "circle", position: { x: 960, y: 540 }, properties: { radius: 18, color: "#111111" } } } ] }`,
|
|
7955
|
+
inputSchema: {
|
|
7956
|
+
type: 'object',
|
|
7957
|
+
properties: {
|
|
7958
|
+
itemId: { type: 'string' },
|
|
7959
|
+
states: { type: 'array', items: { type: 'object', properties: { at: { type: 'number' }, duration: { type: 'number' }, to: { type: 'string' }, shape: { type: 'object', additionalProperties: true } }, required: ['at'] } },
|
|
7960
|
+
easing: { type: 'string', enum: ['linear', 'easeIn', 'easeOut', 'easeInOut'] },
|
|
7961
|
+
camera: { description: 'true, or {fill, easing}', oneOf: [{ type: 'boolean' }, { type: 'object', additionalProperties: true }] },
|
|
7962
|
+
sound: { description: 'true, or a catalogue sound name', oneOf: [{ type: 'boolean' }, { type: 'string' }] },
|
|
7963
|
+
},
|
|
7964
|
+
required: ['itemId', 'states'],
|
|
7965
|
+
},
|
|
7966
|
+
},
|
|
7967
|
+
{
|
|
7968
|
+
name: 'pinepaper_emitter',
|
|
7969
|
+
annotations: { title: 'Particle Emitter', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
7970
|
+
description: `Particles as an ITEM whose every frame is a pure function of its spec and the scene time — so the preview, capture_frames, every export, a saved project, a template and the cloud render all draw the same particles, scrubbing backwards included. The serialisable, cloud-safe choice; pinepaper_apply_effect is a live look on an item.
|
|
7971
|
+
|
|
7972
|
+
ACTIONS:
|
|
7973
|
+
- create: { spec } → itemId
|
|
7974
|
+
- set: { itemId, spec } — merge a partial spec onto the emitter's.
|
|
7975
|
+
|
|
7976
|
+
SPEC:
|
|
7977
|
+
- mode: 'burst' (default) — bursts: [{ t, x, y, count, …per-burst overrides }]: a pop of particles at scene time t.
|
|
7978
|
+
- mode: 'trail' (or give target) — target (an item id), rate (per second), emitWindows [[t0, t1], …], window, offset: particles shed from a moving item.
|
|
7979
|
+
- look and physics (per emitter or per burst): shape ('circle' | 'rect' | 'dash' | 'star' | 'confetti', or an array), palette [colours], size / sizes / sizeEnd, speed + angle ranges or velocities, gravity (px/s², +down), drag, friction, lifetime, fadeIn / fadeOut, spin, opacity, jitter, seed.
|
|
7980
|
+
An unknown shape, mode or bad range is refused with the engine's reason — never defaulted.
|
|
7981
|
+
|
|
7982
|
+
EXAMPLE — confetti at 2 s:
|
|
7983
|
+
{ action: 'create', spec: { bursts: [{ t: 2, x: 960, y: 400, count: 80 }], shape: 'confetti', palette: ['#f43f5e', '#facc15', '#22d3ee'], speed: [300, 700], angle: [-150, -30], gravity: 900, lifetime: 2.5, seed: 7 } }`,
|
|
7984
|
+
inputSchema: {
|
|
7985
|
+
type: 'object',
|
|
7986
|
+
properties: {
|
|
7987
|
+
action: { type: 'string', enum: ['create', 'set'] },
|
|
7988
|
+
itemId: { type: 'string', description: 'set: the emitter' },
|
|
7989
|
+
spec: { type: 'object', additionalProperties: true, description: 'create: the whole spec; set: the keys to change' },
|
|
7990
|
+
},
|
|
7991
|
+
required: ['action', 'spec'],
|
|
7992
|
+
},
|
|
7993
|
+
},
|
|
7994
|
+
{
|
|
7995
|
+
name: 'pinepaper_render_hook',
|
|
7996
|
+
annotations: { title: 'Render Hook', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
7997
|
+
description: `Custom per-frame drawing that a saved scene CARRIES: SOURCE TEXT, compiled and run under the governor's loop guards with seeded Math, saved in the project document, the template and undo, and re-installed on load. (A raw function added with code is not saved — pinepaper_export_scene { full: true } names any such as unserialized.)
|
|
7998
|
+
|
|
7999
|
+
ACTIONS:
|
|
8000
|
+
- register: { id, source, layer? ('above' | 'below'), deterministic? (true), seed? } — source evaluates to (ctx, t, info) => {}: ctx in canvas coordinates, t the scene time in seconds, info { width, height, frame, seed }. 'above' draws over every item, 'below' under them. deterministic reseeds Math.random from (seed, frame) each call, so a frame always draws the same.
|
|
8001
|
+
- unregister: { id } list: the saved hooks.
|
|
8002
|
+
|
|
8003
|
+
Reach for it when no tool draws the look (a scanline overlay, a custom texture pass). For particles, pinepaper_emitter is data rather than code.
|
|
8004
|
+
|
|
8005
|
+
EXAMPLE: { action: 'register', id: 'scanlines', layer: 'above', source: "(ctx, t, info) => { ctx.globalAlpha = 0.08; for (let y = (t * 40) % 4; y < info.height; y += 4) ctx.fillRect(0, y, info.width, 1); }" }`,
|
|
8006
|
+
inputSchema: {
|
|
8007
|
+
type: 'object',
|
|
8008
|
+
properties: {
|
|
8009
|
+
action: { type: 'string', enum: ['register', 'unregister', 'list'] },
|
|
8010
|
+
id: { type: 'string' }, source: { type: 'string' },
|
|
8011
|
+
layer: { type: 'string', enum: ['above', 'below'] },
|
|
8012
|
+
deterministic: { type: 'boolean' }, seed: { type: 'integer' },
|
|
8013
|
+
},
|
|
8014
|
+
required: ['action'],
|
|
8015
|
+
},
|
|
8016
|
+
},
|
|
8017
|
+
{
|
|
8018
|
+
name: 'pinepaper_choreograph',
|
|
8019
|
+
annotations: {
|
|
8020
|
+
title: 'Choreograph an Actor',
|
|
8021
|
+
readOnlyHint: false,
|
|
8022
|
+
destructiveHint: false,
|
|
8023
|
+
idempotentHint: false,
|
|
8024
|
+
openWorldHint: false,
|
|
8025
|
+
},
|
|
8026
|
+
description: `Make any item ACT: a list of beats compiled to one keyframe track with anticipation, squash and stretch, contact and settle — the timing a hand animator would key, from one call.
|
|
8027
|
+
|
|
8028
|
+
BEATS run one after another: [{ at, verb, ...params }]. Positions are the actor's CENTRE in canvas px; a squash keeps its BOTTOM on the ground.
|
|
8029
|
+
- pop: scales up from nothing with an overshoot (an entrance in place).
|
|
8030
|
+
- drop { to? }: falls and lands with a squash. Already in the air (after a fly), it falls from where it is; on the ground or below the landing point, it comes in from above the frame.
|
|
8031
|
+
- hop { to?, height? }: crouch, launch stretched, arc, land squashed, settle. Without to it hops in place.
|
|
8032
|
+
- bounce { times? (3), height?, decay? (0.6), to? }: bounces that lose height; with to it travels while bouncing.
|
|
8033
|
+
- roll { to }: rolls without slipping — it turns by the distance over its radius (right = clockwise).
|
|
8034
|
+
- fly { to, arc? }: an arc through the air, nose up on the rise and down on the fall.
|
|
8035
|
+
- peek { edge (left|right|top|bottom), amount? (0.5), hold? (1) }: slides partly in from a frame edge, waits, ducks back out.
|
|
8036
|
+
- shake { intensity? }: a startled shiver. squash { amount? }: an in-place squash and recover (a reaction, a landing hit).
|
|
8037
|
+
- enter { from (edge), to? }: slides in from an edge with an overshoot. exit { edge }: a small anticipation, then away off that edge.
|
|
8038
|
+
Each verb has a default duration; pass duration to change it.
|
|
8039
|
+
|
|
8040
|
+
WHAT IT RETURNS: keyframes written, the duration, and where the actor ends (and whether that is on screen). Beats that overlap, an unknown verb, or roll / fly without a destination are refused by name.
|
|
8041
|
+
|
|
8042
|
+
ACTORS: a shape, a group, an imported SVG character, a photo cut-out — anything with one transform. Its own scale and rotation are kept (a beat squashes relative to them). A figure placed by pinepaper_character is several parts with their own tracks and no single transform, so choreograph its parts' group only if you grouped a STATIC figure; otherwise direct it with character beats.
|
|
8043
|
+
|
|
8044
|
+
SOUND: sound: true puts a sound on every moment the motion makes — the pop, each launch, every landing (a bounce's landings get quieter as the bounces shrink), a shake — placed on the timeline and baked into the video. sound: { contact: 'kick', whoosh: false, gain: 0.6 } picks or silences them by catalogue name (SFX beep, pop, wind, whoosh, zap; drums kick, snare, tom, hat, clap; or your own from define_sfx). The reply lists the sounds placed. A sound due before 0.06 s is placed at 0.06 s (movedFrom says so): an MP4 encoder swallows the first ~50 ms of audio.
|
|
8045
|
+
|
|
8046
|
+
MIX WITH: pinepaper_character / text expressions on top; pinepaper_apply_effect (confetti, shockwave) at a landing; pinepaper_sound at the same times. Call choreograph BEFORE keyframing the actor's other properties (colour, fill): the beat keys carry position, scale, rotation and opacity only, and the result warns if the actor already animates something else.
|
|
8047
|
+
|
|
8048
|
+
EXAMPLE — a ball comes to life and crosses the frame:
|
|
8049
|
+
{ itemId: "item_1", beats: [ { at: 0, verb: "pop" }, { at: 0.6, verb: "bounce", times: 3, to: [700, 820] }, { at: 2.4, verb: "roll", to: [1200, 820] }, { at: 3.5, verb: "fly", to: [1600, 300] } ] }`,
|
|
8050
|
+
inputSchema: {
|
|
8051
|
+
type: 'object',
|
|
8052
|
+
properties: {
|
|
8053
|
+
itemId: { type: 'string', description: 'The actor (any item with one transform).' },
|
|
8054
|
+
sound: { description: "true, or {pop?, whoosh?, contact?, shake? (catalogue name or false), gain? 0..1}: a sound on every pop, launch, landing and shake.", oneOf: [{ type: 'boolean' }, { type: 'object', additionalProperties: true }] },
|
|
8055
|
+
beats: {
|
|
8056
|
+
type: 'array',
|
|
8057
|
+
description: 'Beats in time order: [{at, verb, to?, duration?, height?, times?, decay?, arc?, edge?, from?, amount?, hold?, intensity?}].',
|
|
8058
|
+
items: {
|
|
8059
|
+
type: 'object',
|
|
8060
|
+
properties: {
|
|
8061
|
+
at: { type: 'number' },
|
|
8062
|
+
verb: { type: 'string', enum: ['pop', 'drop', 'hop', 'bounce', 'roll', 'fly', 'peek', 'shake', 'squash', 'enter', 'exit'] },
|
|
8063
|
+
to: { description: '[x, y] or {x, y}: the actor centre at the end of the beat' },
|
|
8064
|
+
duration: { type: 'number' }, height: { type: 'number' }, times: { type: 'integer' }, decay: { type: 'number' },
|
|
8065
|
+
arc: { type: 'number' }, edge: { type: 'string', enum: ['left', 'right', 'top', 'bottom'] }, from: { type: 'string', enum: ['left', 'right', 'top', 'bottom'] },
|
|
8066
|
+
amount: { type: 'number' }, hold: { type: 'number' }, intensity: { type: 'number' },
|
|
8067
|
+
},
|
|
8068
|
+
required: ['at', 'verb'],
|
|
8069
|
+
},
|
|
8070
|
+
},
|
|
8071
|
+
},
|
|
8072
|
+
required: ['itemId', 'beats'],
|
|
8073
|
+
},
|
|
8074
|
+
},
|
|
7711
8075
|
{
|
|
7712
8076
|
name: 'pinepaper_relight',
|
|
7713
8077
|
annotations: {
|
|
@@ -8104,7 +8468,7 @@ PRIVACY: No screenshots, no tool call arguments, no user content — only struct
|
|
|
8104
8468
|
|
|
8105
8469
|
⚠️ Call ONCE per pipeline. NEVER restart — creates duplicates.
|
|
8106
8470
|
|
|
8107
|
-
WORKFLOW: start_job →
|
|
8471
|
+
WORKFLOW: start_job → the craft tool(s) that fit the brief (styled_scene, story, character, text_style, compose, design_medium…) → batch_execute for the primitive glue, in ONE call → end_job (screenshot) → show user → iterate if needed. Primitive shapes over a generator backdrop is the stock look; use it only when no higher-level tool fits.
|
|
8108
8472
|
|
|
8109
8473
|
RETURNS: canvasSize {width, height} — use this to position items within bounds. Without a canvasPreset, an unbounded canvas is fixed at that size, so stills and video both export it (the result says so in note). Also returns ontology: a pp: triples + item summary snapshot of the (typically empty) canvas. Reuse this baseline across the same agent turn instead of calling pinepaper_get_canvas_ontology unless you've mutated the canvas. When clearCanvas is false, the ontology captures whatever's already on the canvas.
|
|
8110
8474
|
|
|
@@ -8257,7 +8621,7 @@ EXAMPLES:
|
|
|
8257
8621
|
idempotentHint: false,
|
|
8258
8622
|
openWorldHint: false,
|
|
8259
8623
|
},
|
|
8260
|
-
description: `Execute
|
|
8624
|
+
description: `Execute the primitive operations of a scene in a single call — canvas setup, backgrounds, items, animations, masks, effects, and playback. It is the GLUE: a styled scene, a character, a story or a crafted title has its own tool (styled_scene, character, story, text_style), called first.
|
|
8261
8625
|
|
|
8262
8626
|
NOTE: For diagrams (flowcharts, UML), use pinepaper_create_diagram_shape + pinepaper_connect instead.
|
|
8263
8627
|
|
|
@@ -8268,7 +8632,7 @@ NOTE: For diagrams (flowcharts, UML), use pinepaper_create_diagram_shape + pinep
|
|
|
8268
8632
|
ONTOLOGY PREFLIGHT: Before code generation, this tool validates itemType, relationType, effectType, and generatorName against the canonical PinePaper vocabulary. A typo (e.g. "circel") returns a structured VALIDATION_ERROR with the operation index, the offending field, the canonical valid list, and a suggested fix — fix and retry rather than guessing. Pass skipValidation: true to bypass when intentionally using experimental vocabulary.
|
|
8269
8633
|
|
|
8270
8634
|
WORKFLOW:
|
|
8271
|
-
1. pinepaper_agent_start_job → 2.
|
|
8635
|
+
1. pinepaper_agent_start_job → 2. the craft tools that fit the brief (styled_scene, story, character, text_style, compose…) → 3. pinepaper_agent_batch_execute (the primitive glue, in one call) → 4. pinepaper_agent_end_job (screenshot for validation)
|
|
8272
8636
|
After validation: user reviews screenshot → feedback → modify/recreate as needed.
|
|
8273
8637
|
|
|
8274
8638
|
OPERATION TYPES (${AgentBatchOperationTypeSchema.options.length}) — use in this order:
|
|
@@ -8298,7 +8662,7 @@ ITEMS:
|
|
|
8298
8662
|
ANIMATION:
|
|
8299
8663
|
animate — Loop animation: {itemId, animationType, animationOptions}
|
|
8300
8664
|
animationType: ${SIMPLE_ANIMATION_TYPES_LIST}
|
|
8301
|
-
keyframe_animate — Timed animation: {itemId, keyframes: [{time, properties, easing}], duration, loop}
|
|
8665
|
+
keyframe_animate — Timed animation: {itemId, keyframes: [{time, properties, easing}], duration, loop, append}. Replaces the item's track unless append: true; two ops on one item in a batch are merged into one.
|
|
8302
8666
|
Animatable properties: opacity, scale, scaleX, scaleY, x, y, rotation, fillColor, strokeColor, strokeWidth, width, height, radius, fontSize, trimStart/trimEnd/trimOffset (path draw-on)
|
|
8303
8667
|
Easing (on the keyframe being moved TO): ${KEYFRAME_EASINGS_LIST}
|
|
8304
8668
|
relation — Behavioral link: {sourceId, targetId, relationType, relationOptions}
|
|
@@ -8354,6 +8718,7 @@ EXAMPLE — Animated sky scene with timed reveals:
|
|
|
8354
8718
|
height: { type: 'number', description: 'Canvas height for set_canvas_size (100-16384)' },
|
|
8355
8719
|
preset: { type: 'string', description: 'Canvas preset for set_canvas_size' },
|
|
8356
8720
|
backgroundColor: { type: 'string', description: 'For set_background: hex color' },
|
|
8721
|
+
color: { type: 'string', description: 'For set_background: same as backgroundColor. For create: same as fillColor.' },
|
|
8357
8722
|
generatorName: {
|
|
8358
8723
|
type: 'string',
|
|
8359
8724
|
enum: [...GeneratorNameSchema.options],
|
|
@@ -8400,6 +8765,7 @@ EXAMPLE — Animated sky scene with timed reveals:
|
|
|
8400
8765
|
},
|
|
8401
8766
|
duration: { type: 'number', description: 'Duration in seconds for keyframe_animate or play_timeline' },
|
|
8402
8767
|
loop: { type: 'boolean', description: 'Loop for keyframe_animate or play_timeline' },
|
|
8768
|
+
append: { type: 'boolean', description: "For keyframe_animate: add these keys to the item's existing track instead of replacing it (a key at the same time merges, the new values win). Without it the op replaces the track; the op's result says which happened." },
|
|
8403
8769
|
// Relations
|
|
8404
8770
|
sourceId: { type: 'string', description: 'For relation: source item ID or $N' },
|
|
8405
8771
|
targetId: { type: 'string', description: 'For relation: target item ID or $N' },
|
|
@@ -9403,8 +9769,11 @@ ACTIONS:
|
|
|
9403
9769
|
},
|
|
9404
9770
|
},
|
|
9405
9771
|
// ---------------------------------------------------------------------------
|
|
9406
|
-
// STITCHCRAFT PROCEDURAL EMBROIDERY & THREAD
|
|
9772
|
+
// STITCHCRAFT PROCEDURAL EMBROIDERY & THREAD, SUBDIVISION, UI KIT
|
|
9773
|
+
// The cloud's craft tools, shared: schemas vendored from mcp-cloud, local
|
|
9774
|
+
// words and behaviour in craft-tools.ts.
|
|
9407
9775
|
// ---------------------------------------------------------------------------
|
|
9776
|
+
...craftToolDefinitions(),
|
|
9408
9777
|
];
|
|
9409
9778
|
/**
|
|
9410
9779
|
* Get a tool definition by name
|