@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.
Files changed (113) hide show
  1. package/README.de.md +1 -1
  2. package/README.es.md +1 -1
  3. package/README.fr.md +1 -1
  4. package/README.hi.md +1 -1
  5. package/README.ja.md +1 -1
  6. package/README.ko.md +1 -1
  7. package/README.md +161 -5
  8. package/README.pt-BR.md +1 -1
  9. package/README.zh-CN.md +1 -1
  10. package/dist/build-info.json +2 -2
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7 -4
  13. package/dist/index.js.map +1 -1
  14. package/dist/ontology/upstream-vocabulary.d.ts +2 -2
  15. package/dist/ontology/upstream-vocabulary.js +3 -3
  16. package/dist/ontology/upstream-vocabulary.js.map +1 -1
  17. package/dist/ontology/vocabulary.d.ts.map +1 -1
  18. package/dist/ontology/vocabulary.js +9 -1
  19. package/dist/ontology/vocabulary.js.map +1 -1
  20. package/dist/tools/choreograph.d.ts +64 -0
  21. package/dist/tools/choreograph.d.ts.map +1 -0
  22. package/dist/tools/choreograph.js +346 -0
  23. package/dist/tools/choreograph.js.map +1 -0
  24. package/dist/tools/compact-descriptions.js +1 -1
  25. package/dist/tools/craft-tools.d.ts +55 -0
  26. package/dist/tools/craft-tools.d.ts.map +1 -0
  27. package/dist/tools/craft-tools.js +139 -0
  28. package/dist/tools/craft-tools.js.map +1 -0
  29. package/dist/tools/definitions.d.ts +10 -1
  30. package/dist/tools/definitions.d.ts.map +1 -1
  31. package/dist/tools/definitions.js +401 -32
  32. package/dist/tools/definitions.js.map +1 -1
  33. package/dist/tools/effect-check.d.ts +26 -0
  34. package/dist/tools/effect-check.d.ts.map +1 -0
  35. package/dist/tools/effect-check.js +129 -0
  36. package/dist/tools/effect-check.js.map +1 -0
  37. package/dist/tools/effect-class.d.ts +31 -0
  38. package/dist/tools/effect-class.d.ts.map +1 -0
  39. package/dist/tools/effect-class.js +176 -0
  40. package/dist/tools/effect-class.js.map +1 -0
  41. package/dist/tools/engine-lists.d.ts +6 -6
  42. package/dist/tools/engine-lists.d.ts.map +1 -1
  43. package/dist/tools/engine-lists.js +11 -9
  44. package/dist/tools/engine-lists.js.map +1 -1
  45. package/dist/tools/engine-methods.d.ts.map +1 -1
  46. package/dist/tools/engine-methods.js +12 -0
  47. package/dist/tools/engine-methods.js.map +1 -1
  48. package/dist/tools/engine-surface.d.ts +1 -1
  49. package/dist/tools/engine-surface.d.ts.map +1 -1
  50. package/dist/tools/engine-surface.js +221 -10
  51. package/dist/tools/engine-surface.js.map +1 -1
  52. package/dist/tools/handlers/export.d.ts.map +1 -1
  53. package/dist/tools/handlers/export.js +8 -1
  54. package/dist/tools/handlers/export.js.map +1 -1
  55. package/dist/tools/handlers/generate.d.ts +28 -28
  56. package/dist/tools/handlers.d.ts +23 -0
  57. package/dist/tools/handlers.d.ts.map +1 -1
  58. package/dist/tools/handlers.js +301 -6
  59. package/dist/tools/handlers.js.map +1 -1
  60. package/dist/tools/index.d.ts +2 -2
  61. package/dist/tools/index.d.ts.map +1 -1
  62. package/dist/tools/index.js +2 -2
  63. package/dist/tools/index.js.map +1 -1
  64. package/dist/tools/minimal-descriptions.d.ts.map +1 -1
  65. package/dist/tools/minimal-descriptions.js +16 -3
  66. package/dist/tools/minimal-descriptions.js.map +1 -1
  67. package/dist/tools/shape-params.d.ts +2 -2
  68. package/dist/tools/shape-params.d.ts.map +1 -1
  69. package/dist/tools/shape-params.js +25 -2
  70. package/dist/tools/shape-params.js.map +1 -1
  71. package/dist/tools/stick-vocabulary.d.ts +1 -1
  72. package/dist/tools/stick-vocabulary.js +1 -1
  73. package/dist/tools/toolkits.d.ts.map +1 -1
  74. package/dist/tools/toolkits.js +15 -0
  75. package/dist/tools/toolkits.js.map +1 -1
  76. package/dist/types/code-generator.d.ts +44 -1
  77. package/dist/types/code-generator.d.ts.map +1 -1
  78. package/dist/types/code-generator.js +1140 -128
  79. package/dist/types/code-generator.js.map +1 -1
  80. package/dist/types/schemas.d.ts +3091 -584
  81. package/dist/types/schemas.d.ts.map +1 -1
  82. package/dist/types/schemas.js +308 -6
  83. package/dist/types/schemas.js.map +1 -1
  84. package/dist/vendor/design/design-style.d.ts +2 -2
  85. package/dist/vendor/design/design-style.d.ts.map +1 -1
  86. package/dist/vendor/design/design-style.js +7 -2
  87. package/dist/vendor/design/design-style.js.map +1 -1
  88. package/dist/vendor/design/subdivision-tool-schema.d.ts +16 -0
  89. package/dist/vendor/design/subdivision-tool-schema.d.ts.map +1 -0
  90. package/dist/vendor/design/subdivision-tool-schema.js +104 -0
  91. package/dist/vendor/design/subdivision-tool-schema.js.map +1 -0
  92. package/dist/vendor/design/textile-tool-schema.d.ts +14 -0
  93. package/dist/vendor/design/textile-tool-schema.d.ts.map +1 -0
  94. package/dist/vendor/design/textile-tool-schema.js +195 -0
  95. package/dist/vendor/design/textile-tool-schema.js.map +1 -0
  96. package/dist/vendor/design/ui-kit-runtime.d.ts +49 -0
  97. package/dist/vendor/design/ui-kit-runtime.d.ts.map +1 -0
  98. package/dist/vendor/design/ui-kit-runtime.js +761 -0
  99. package/dist/vendor/design/ui-kit-runtime.js.map +1 -0
  100. package/dist/vendor/design/ui-kit-tool-schema.d.ts +280 -0
  101. package/dist/vendor/design/ui-kit-tool-schema.d.ts.map +1 -0
  102. package/dist/vendor/design/ui-kit-tool-schema.js +209 -0
  103. package/dist/vendor/design/ui-kit-tool-schema.js.map +1 -0
  104. package/manifest.json +53 -1
  105. package/package.json +5 -4
  106. package/dist/assets/repositories/openclipart.d.ts +0 -41
  107. package/dist/assets/repositories/openclipart.d.ts.map +0 -1
  108. package/dist/assets/repositories/openclipart.js +0 -139
  109. package/dist/assets/repositories/openclipart.js.map +0 -1
  110. package/dist/assets/repositories/svgrepo.d.ts +0 -61
  111. package/dist/assets/repositories/svgrepo.d.ts.map +0 -1
  112. package/dist/assets/repositories/svgrepo.js +0 -276
  113. package/dist/assets/repositories/svgrepo.js.map +0 -1
@@ -78,8 +78,18 @@ export const BlendModeSchema = z.enum([
78
78
  * one table, so every easing enum here is this list.
79
79
  */
80
80
  export const KEYFRAME_EASINGS = ENGINE_EASING_NAMES;
81
+ /**
82
+ * Lines for easings an engine branch adds before the list is resynced
83
+ * (FxTool D11: hold / step). Spread in, so they satisfy the record the moment
84
+ * the sync brings the names, and are unused until then.
85
+ */
86
+ const UPCOMING_EASING_DESCRIPTIONS = {
87
+ hold: 'Holds the previous value, then jumps at the next key (a cut, no in-between)',
88
+ step: 'Same as hold: jumps at the next key',
89
+ };
81
90
  /** One line per easing, for tool descriptions and pinepaper_get_available_easings. */
82
91
  export const EASING_DESCRIPTIONS = {
92
+ ...UPCOMING_EASING_DESCRIPTIONS,
83
93
  linear: 'Constant speed',
84
94
  easeIn: 'Slow start, fast end',
85
95
  easeOut: 'Fast start, slow end',
@@ -195,9 +205,8 @@ export const ItemTypeSchema = z.enum([
195
205
  // and no tool has ever offered them, so the only way to reach one was to
196
206
  // hand-write a scene file.
197
207
  //
198
- // In the browser they degrade to a flat plate of their own colour (see
199
- // generateCreateItemCode) so a composition built with one still reads
200
- // locally; the shaded surface appears in a cloud render.
208
+ // Created as a tagged plate (see generateCreateItemCode) that the studio and
209
+ // the cloud renderer both draw the surface over.
201
210
  'shader',
202
211
  'field',
203
212
  ]);
@@ -316,6 +325,8 @@ export const KeyframeSchema = z.object({
316
325
  time: z.number().describe('Time in seconds for this keyframe'),
317
326
  properties: z.record(z.unknown()).describe('Property values at this keyframe'),
318
327
  easing: KeyframeEasingSchema.optional().default('linear'),
328
+ interpolation: z.literal('hold').optional().describe("'hold': this key holds ITS OWN value until the next key, then jumps (FxTool D11). Refused on a studio without hold keys."),
329
+ hold: z.boolean().optional().describe("Same as interpolation: 'hold'."),
319
330
  });
320
331
  // =============================================================================
321
332
  // RELATION TYPES
@@ -396,6 +407,10 @@ export const RelationTypeSchema = z.enum([
396
407
  'on_event_toggle',
397
408
  'on_event_store_set',
398
409
  'on_event_store_increment',
410
+ 'on_event_play_sound',
411
+ 'looks_at',
412
+ 'reacts_to',
413
+ 'drag_to_turn',
399
414
  // --- Proximity — the pointer entering or leaving an item ---
400
415
  'on_enter_set_color',
401
416
  'on_enter_set_property',
@@ -724,6 +739,7 @@ export const EventInputSchema = z.object({
724
739
  payloadType: z.string().optional().describe("Payload type label (default 'Pulse') — action 'create'"),
725
740
  x: z.number().optional().describe('Canvas x for the event marker — action create'),
726
741
  y: z.number().optional().describe('Canvas y for the event marker — action create'),
742
+ at: z.union([z.number().min(0), z.array(z.number().min(0)).min(1)]).optional().describe('create: fire the event ITSELF at these scene times (seconds) — a timed trigger, so on_event_play_sound and every other reaction runs on the timeline and in video exports.'),
727
743
  eventId: z.string().optional().describe("Event id to fire — required for action 'pulse'"),
728
744
  payload: z.unknown().optional().describe("Optional value forwarded to listeners — action 'pulse'"),
729
745
  }).refine((v) => v.action !== 'create' || !!v.name, { message: "action 'create' requires name", path: ['name'] })
@@ -1150,6 +1166,40 @@ export const ItemDataFlagsSchema = z.object({
1150
1166
  isDraggable: z.boolean().optional(),
1151
1167
  isDecorative: z.boolean().optional(),
1152
1168
  });
1169
+ /**
1170
+ * Per-item engine setters applied after create / modify (FxTool D20, D11).
1171
+ * Sent through their own engine calls, never as create params: an engine
1172
+ * without them would ignore an unknown param in silence, and the call says so
1173
+ * instead.
1174
+ */
1175
+ export const ScreenSpaceSchema = z.boolean()
1176
+ .describe('HUD layer: true keeps the item fixed in the FRAME under camera moves (its x/y, keyframes and relations are then frame coordinates). Exports honour it; the live preview still tilts it; SVG and Lottie ignore it.');
1177
+ export const StepTimingSchema = z.union([
1178
+ z.number().int().min(1),
1179
+ z.object({ every: z.number().int().min(1), baseFps: z.number().positive().optional() }).strict(),
1180
+ z.object({ fps: z.number().positive() }).strict(),
1181
+ z.null(),
1182
+ ]).describe("Animate this item 'on twos' (or threes…): N or {every: N, baseFps?} steps every N frames of a 30 fps base unless baseFps is given; {fps} steps at that rate; null turns it off. A stepped group steps its children. Not stepped: follows, spring_follow, auras, rigging and GPU generators.");
1183
+ /** A text item's counter (FxTool D70): a number that counts on the scene clock. */
1184
+ export const CounterSchema = z.union([
1185
+ z.object({
1186
+ from: z.number(), to: z.number(),
1187
+ start: z.number().min(0).optional().describe('Seconds into the timeline (default 0).'),
1188
+ duration: z.number().positive().optional().describe('Seconds to count (default 1).'),
1189
+ easing: z.union([z.string(), z.tuple([z.number(), z.number(), z.number(), z.number()])]).optional().describe("Any engine easing (including 'hold' / 'step') or a cubic Bézier [x1, y1, x2, y2]; default linear."),
1190
+ decimals: z.number().int().min(0).max(10).optional().describe('Default: the precision from / to were written with.'),
1191
+ prefix: z.string().optional(), suffix: z.string().optional(),
1192
+ locale: z.string().optional().describe("Number format locale (default 'en-US')."),
1193
+ grouping: z.boolean().optional().describe('Thousands separators (default true).'),
1194
+ padStart: z.number().int().min(1).max(21).optional().describe('Zero-pad to this many digits.'),
1195
+ steps: z.number().int().positive().optional().describe('Count in N jumps instead of smoothly.'),
1196
+ tabular: z.boolean().optional().describe('Fixed-width digits so the width stays steady (default true).'),
1197
+ era: z.boolean().optional().describe("Years with an era: a negative prints as its magnitude + eraNegative ('30,000 BCE'), a positive + eraPositive ('2026 CE'). No year 0: the count crosses 1 BCE → 1 CE. Years group from five digits only ('1274 BCE'); grouping: false turns that off; prefix / suffix still wrap it."),
1198
+ eraNegative: z.string().optional().describe("Era label for negative years (default 'BCE')."),
1199
+ eraPositive: z.string().optional().describe("Era label for positive years (default 'CE'; '' for a bare year; 'AD' works)."),
1200
+ }).strict(),
1201
+ z.null(),
1202
+ ]).describe("A text that COUNTS on the scene clock: {from, to, start?, duration?, easing?, decimals?, prefix?, suffix?, locale?, grouping?, padStart?, steps?, tabular?}; null removes it. Exact on every exported frame. To shape the count by hand, keyframe the numeric property counterValue instead.");
1153
1203
  export const CreateItemInputSchema = z.object({
1154
1204
  itemType: ItemTypeSchema,
1155
1205
  position: PositionSchema.optional().default({ x: 400, y: 300 }),
@@ -1164,6 +1214,9 @@ export const CreateItemInputSchema = z.object({
1164
1214
  // is read beside position as often as inside properties — and at the top
1165
1215
  // level it was stripped without a word (round 7 retest, 1.63).
1166
1216
  anchor: z.string().optional().describe("Which point position names: 'center' (default), 'top-left', 'top-right', 'bottom-left', 'bottom-right'. Same as properties.anchor."),
1217
+ screenSpace: ScreenSpaceSchema.optional(),
1218
+ stepTiming: StepTimingSchema.optional(),
1219
+ counter: CounterSchema.optional(),
1167
1220
  });
1168
1221
  // Light direction for 3D effects
1169
1222
  export const LightDirectionSchema = z.enum([
@@ -1203,6 +1256,9 @@ export const ModifyItemInputSchema = z.object({
1203
1256
  data: ItemDataFlagsSchema.optional(),
1204
1257
  atTime: z.number().min(0).optional().describe('AUTO-KEY: write these properties as keyframes at this time (seconds) instead of a static edit — eased from the previous value, other keys kept.'),
1205
1258
  easing: z.string().optional().describe('atTime: easing of the segment arriving at the new key (default linear).'),
1259
+ screenSpace: ScreenSpaceSchema.optional(),
1260
+ stepTiming: StepTimingSchema.optional(),
1261
+ counter: CounterSchema.optional(),
1206
1262
  });
1207
1263
  // Delete Item
1208
1264
  export const DeleteItemInputSchema = z.object({
@@ -1300,6 +1356,34 @@ export const CharacterInputSchema = z.object({
1300
1356
  ink: z.string().optional()
1301
1357
  .describe("Ink colour for the stroked copies that make the figure read as drawn rather than filled. The style supplies its own; this overrides it."),
1302
1358
  });
1359
+ // Character tool aliases (compat with on-device / local model specs)
1360
+ export const CreateCharacterInputSchema = z.object({
1361
+ characterId: z.string().optional().describe('Unique character identifier'),
1362
+ name: z.string().optional().describe('Character display name'),
1363
+ x: z.number().optional().describe('X coordinate on canvas (default: 400)'),
1364
+ y: z.number().optional().describe('Y coordinate on canvas (default: 300)'),
1365
+ scale: z.number().optional().describe('Scale multiplier (default: 1)'),
1366
+ preset: z.string().optional().describe('Character preset/concept name (e.g. pp:Pigeon, pp:Character, person, robot)'),
1367
+ concept: z.string().optional().describe('Alias for preset'),
1368
+ base: z.string().optional().describe('Base rig name for original character (person, robot, cat, bird, blob)'),
1369
+ variant: z.record(z.string(), z.unknown()).optional().describe('Variant parameters'),
1370
+ bundle: z.record(z.string(), z.unknown()).optional().describe('Layered character bundle manifest'),
1371
+ }).passthrough();
1372
+ export const CharacterActionInputSchema = z.object({
1373
+ characterId: z.string().optional().describe('Character identifier'),
1374
+ itemId: z.string().optional().describe('Item identifier (alias for characterId)'),
1375
+ action: z.string().default('walk').describe('Locomotion or motion action: walk, run, locomotion, wave, dance, jump, idle'),
1376
+ speed: z.number().optional().describe('Speed in pixels/sec (default: 100)'),
1377
+ direction: z.number().optional().describe('Movement direction (1 or -1)'),
1378
+ duration: z.number().optional().describe('Duration in seconds'),
1379
+ }).passthrough();
1380
+ export const CharacterPoseInputSchema = z.object({
1381
+ characterId: z.string().optional().describe('Character identifier'),
1382
+ itemId: z.string().optional().describe('Item identifier (alias for characterId)'),
1383
+ expression: z.string().default('smile').describe('Facial expression: blink, frown, smile, surprise, talk, wink'),
1384
+ duration: z.number().nullable().optional().describe('Duration in seconds'),
1385
+ interval: z.number().nullable().optional().describe('Repeat interval in seconds'),
1386
+ }).passthrough();
1303
1387
  // Motion Capture (BVH)
1304
1388
  //
1305
1389
  // FxTool exposes two distinct operations and the difference is the whole point:
@@ -2066,6 +2150,9 @@ export const AgentBatchOperationSchema = z.object({
2066
2150
  // PositionSchema, as create_item takes: [x, y] was refused here (1.6.19 gate).
2067
2151
  position: PositionSchema.optional().describe('Position for create operations: {x, y} or [x, y]'),
2068
2152
  properties: z.record(z.unknown()).optional().describe('Properties for create/modify operations'),
2153
+ screenSpace: ScreenSpaceSchema.optional().describe('create: keep the item fixed in the frame under camera moves (HUD).'),
2154
+ stepTiming: StepTimingSchema.optional().describe("create: step this item's animation (N, {every, baseFps?} or {fps})."),
2155
+ counter: CounterSchema.optional().describe('create (text): count on the scene clock.'),
2069
2156
  // Modify/Animate/Delete/Keyframe/Mask/Effect target
2070
2157
  itemId: z.string().optional().describe('Target item ID or $N reference'),
2071
2158
  // Group operation fields
@@ -2081,6 +2168,7 @@ export const AgentBatchOperationSchema = z.object({
2081
2168
  relationOptions: z.record(z.unknown()).optional().describe('Relation options'),
2082
2169
  // Set background fields
2083
2170
  backgroundColor: z.string().optional().describe('Background color for set_background'),
2171
+ color: z.string().optional().describe('Alias for backgroundColor in set_background, or color property'),
2084
2172
  // Execute generator fields
2085
2173
  generatorName: z.string().optional().describe('Generator name for execute_generator'),
2086
2174
  generatorParams: z.record(z.unknown()).optional().describe('Generator parameters'),
@@ -2097,6 +2185,7 @@ export const AgentBatchOperationSchema = z.object({
2097
2185
  })).optional().describe('Keyframes for keyframe_animate [{time, properties, easing}]'),
2098
2186
  duration: z.number().optional().describe('Duration in seconds for keyframe_animate or play_timeline'),
2099
2187
  loop: z.boolean().optional().describe('Loop for keyframe_animate or play_timeline'),
2188
+ append: z.boolean().optional().describe("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."),
2100
2189
  // Apply mask fields
2101
2190
  maskPreset: z.string().optional().describe('Mask preset for apply_mask (wipeLeft, wipeRight, wipeUp, wipeDown, iris, irisOut, star, heart, curtainHorizontal, curtainVertical, cinematic, diagonalWipe, revealUp, revealDown)'),
2102
2191
  maskType: z.string().optional().describe('Mask shape for apply_mask (rectangle, circle, ellipse, star, triangle, hexagon, heart, rounded)'),
@@ -2107,7 +2196,7 @@ export const AgentBatchOperationSchema = z.object({
2107
2196
  // Play timeline fields
2108
2197
  action: z.string().optional().describe('Timeline action for play_timeline (play, stop, seek)'),
2109
2198
  time: z.number().optional().describe('Seek time for play_timeline'),
2110
- }).describe('Single batch operation');
2199
+ }).passthrough().describe('Single batch operation');
2111
2200
  // Group / ungroup — combine the parts of a composite object into ONE draggable entity
2112
2201
  // (a car's body + wheels + windows), or break a group back into loose items.
2113
2202
  export const GroupInputSchema = z.object({
@@ -2914,6 +3003,8 @@ export const ScenePlaybackInputSchema = z.object({
2914
3003
  defaultDuration: z.number().optional(),
2915
3004
  defaultTransition: z.enum(['none', 'fade', 'zoom-in', 'zoom-out']).optional(),
2916
3005
  transitionDuration: z.number().optional(),
3006
+ cutSound: z.union([z.string().min(1), z.object({ sound: z.string().min(1), gain: z.number().min(0).optional(), offset: z.number().optional() }).strict()]).optional()
3007
+ .describe("create_chain: a sound at every cut — a catalogue name, or {sound, gain?, offset? (seconds, + later / − earlier)}. The scene_cut event also fires at each cut ({from, to, time, index})."),
2917
3008
  enabled: z.boolean().optional(),
2918
3009
  index: z.number().optional(),
2919
3010
  });
@@ -3118,7 +3209,7 @@ export const TransformInputSchema = z.object({
3118
3209
  // `pinepaper_media` / `pinepaper_transform` set the precedent.
3119
3210
  export const BrandKitInputSchema = z.object({
3120
3211
  /** 'plan' reports what WOULD change without touching the scene. 'from_url' reads a site into a proposed kit. */
3121
- action: z.enum(['plan', 'apply', 'from_url']),
3212
+ action: z.enum(['plan', 'apply', 'from_url', 'set', 'get', 'clear']),
3122
3213
  url: z.string().url().optional().describe("from_url: the product's site (http/https). Read in an isolated browser context: the studio is not navigated."),
3123
3214
  screenshots: z.number().int().min(1).max(6).optional().describe('from_url: screenshots taken down the page (default 3).'),
3124
3215
  // Spelled out rather than a loose record. A first version typed this as
@@ -3144,7 +3235,7 @@ export const BrandKitInputSchema = z.object({
3144
3235
  }).optional(),
3145
3236
  selectionOnly: z.boolean().optional(),
3146
3237
  })
3147
- .refine((v) => v.action === 'from_url' || !!v.kit, { message: 'plan and apply require kit (from_url proposes one)', path: ['kit'] })
3238
+ .refine((v) => !['plan', 'apply', 'set'].includes(v.action) || !!v.kit, { message: 'plan, apply and set require kit (from_url proposes one)', path: ['kit'] })
3148
3239
  .refine((v) => v.action !== 'from_url' || (!!v.url && /^https?:\/\//i.test(v.url)), { message: 'from_url requires an http(s) url', path: ['url'] });
3149
3240
  export const ComponentInputSchema = z.object({
3150
3241
  action: z.enum(['define', 'list', 'instantiate', 'set_override', 'sync', 'update_from_instance', 'detach']),
@@ -3403,6 +3494,33 @@ export const DeformInputSchema = z.object({
3403
3494
  speed: z.number().optional(),
3404
3495
  steps: z.number().optional(),
3405
3496
  });
3497
+ // Procedural Hand-Drawn Canvas Animation (Sketchy, Wobble, Stop-Motion, Reveal)
3498
+ export const HandDrawnInputSchema = z.object({
3499
+ itemId: z.string().optional().describe('Target item ID (defaults to active selection)'),
3500
+ style: z.enum(['sketchy', 'reveal', 'ink_reveal', 'wobble', 'stop_motion']).optional().describe("Hand-drawn style (default 'sketchy'). wobble and stop_motion are refused with the pinepaper_design_medium call that does it"),
3501
+ mode: z.enum(['sketchy', 'reveal', 'ink_reveal', 'wobble', 'stop_motion']).optional().describe('Alias for style'),
3502
+ roughness: z.number().optional().default(2).describe('Noise magnitude in pixels (default: 2)'),
3503
+ bowing: z.number().optional().default(1).describe('Line bowing curve handle factor (default: 1)'),
3504
+ fps: z.number().optional().describe('wobble/stop_motion: the boil rate handed to pinepaper_design_medium (default: 12)'),
3505
+ posterizeFps: z.number().optional().describe('Alias for fps'),
3506
+ seed: z.number().optional().default(1).describe('PRNG seed for deterministic vertex displacement (default: 1)'),
3507
+ duration: z.number().positive().optional().describe('reveal: seconds to draw the stroke on (default: 2)'),
3508
+ });
3509
+ // Pen figures (FxTool 671d8a58): app.createFigure builds the ink and a pen and
3510
+ // joins them with draws_figure; the catalogue is the engine's (app.listFigures).
3511
+ export const PenFigureInputSchema = z.object({
3512
+ action: z.enum(['create', 'list']).optional().describe("'create' (default) draws a figure; 'list' returns the catalogue: id, title and kind."),
3513
+ figure: z.union([z.string(), z.record(z.unknown())]).optional().describe("create: a catalogue id from action 'list' (e.g. 'lorenz', 'lissajous', 'hilbert', 'harmonograph'), or an inline figure spec object."),
3514
+ box: z.object({ x: z.number(), y: z.number(), width: z.number().positive(), height: z.number().positive() }).strict().optional().describe('Where to draw, in canvas px (default: the whole canvas). The figure is fitted by one uniform scale.'),
3515
+ margin: z.number().min(0).max(0.45).optional().describe('Fraction of the box left clear (default 0.06).'),
3516
+ duration: z.number().positive().optional().describe('Seconds the pen takes for the whole figure, pen-up travel included (default 6).'),
3517
+ delay: z.number().min(0).optional().describe('Seconds before the pen starts (default 0).'),
3518
+ loop: z.boolean().optional().describe('Draw the figure again after it finishes (default false: it stays drawn).'),
3519
+ pen: z.boolean().optional().describe('false: the ink only, fully drawn, no pen and no animation (default true).'),
3520
+ strokeColor: z.string().optional().describe("Ink colour (default '#1e293b')."),
3521
+ penColor: z.string().optional().describe("Pen tip colour (default '#e11d48')."),
3522
+ strokeWidth: z.number().positive().optional().describe('Ink width in px (default 2).'),
3523
+ }).strict();
3406
3524
  // =============================================================================
3407
3525
  // SPRITE SHEET TOOLS
3408
3526
  // =============================================================================
@@ -3952,6 +4070,105 @@ export const RelightInputSchema = z.object({
3952
4070
  preview: z.union([z.boolean(), z.object({ maxSide: z.number().min(64).max(4096) })]).optional()
3953
4071
  .describe('set: show it in the live preview (capped-resolution overlay); {maxSide} sets the cap.'),
3954
4072
  }).refine((v) => v.action !== 'set' || (Array.isArray(v.lights) && v.lights.length > 0) || v.enabled === false, { message: 'set requires at least one light (or enabled: false)', path: ['lights'] });
4073
+ /** pinepaper_emitter — the data-driven particle item (FxTool D29). The engine validates the spec and refuses by name. */
4074
+ export const EmitterInputSchema = z.object({
4075
+ action: z.enum(['create', 'set']),
4076
+ itemId: z.string().optional().describe('set: the emitter to change.'),
4077
+ spec: z.record(z.string(), z.unknown()).describe('create: the whole spec. set: the keys to change (merged).'),
4078
+ }).refine((v) => v.action !== 'set' || !!v.itemId, { message: 'set requires itemId', path: ['itemId'] });
4079
+ /** pinepaper_look (D64): restyle an item, a subtree or the whole scene as a named look. */
4080
+ export const LookInputSchema = z.object({
4081
+ action: z.enum(['list', 'describe', 'apply', 'remove', 'get', 'grid']),
4082
+ look: z.union([z.string().min(1), z.record(z.string(), z.unknown())]).optional().describe('describe / apply: a look id from list, or a look spec.'),
4083
+ target: z.string().optional().describe("apply / remove / get: an itemId, or 'scene' (default)."),
4084
+ scope: z.enum(['item', 'subtree', 'scene']).optional().describe('apply: how far the look reaches (default: the item for an id, the scene for scene).'),
4085
+ looks: z.array(z.string()).optional().describe('grid: which looks (default every one).'),
4086
+ cols: z.number().int().positive().optional(), cellWidth: z.number().positive().optional(), t: z.number().min(0).optional(),
4087
+ })
4088
+ .refine((v) => !['describe', 'apply'].includes(v.action) || v.look !== undefined, { message: 'describe and apply require look', path: ['look'] });
4089
+ /** pinepaper_original_character (D66): an ORIGINAL character from a base rig and a declarative variant. */
4090
+ export const OriginalCharacterInputSchema = z.object({
4091
+ action: z.enum(['bases', 'describe', 'random', 'create']),
4092
+ base: z.string().optional().describe('person | robot | cat | bird | blob (bases lists them).'),
4093
+ variant: z.record(z.string(), z.unknown()).optional().describe('create: {palette, features, look, seed, name} — legal values from describe.'),
4094
+ seed: z.number().int().optional().describe('random: same (base, seed) → the same variant.'),
4095
+ fixed: z.record(z.string(), z.unknown()).optional().describe('random: traits every member shares (a look, proportions), merged over the random choice.'),
4096
+ at: z.object({ x: z.number(), y: z.number() }).optional().describe('create: x, and y = the FEET.'),
4097
+ height: z.number().positive().optional().describe('create: drawn height px (default 320).'),
4098
+ scale: z.number().positive().optional().describe('create: multiplier of the default height.'),
4099
+ rig: z.boolean().optional().describe('create: build the skeleton (default true).'),
4100
+ })
4101
+ .refine((v) => !['random', 'create'].includes(v.action) || !!v.base, { message: 'random and create require base', path: ['base'] })
4102
+ .refine((v) => v.action !== 'random' || v.seed !== undefined, { message: 'random requires seed', path: ['seed'] });
4103
+ /** pinepaper_morph_sequence (D73): one element through N states as a morphs_to chain. */
4104
+ export const MorphSequenceInputSchema = z.object({
4105
+ itemId: z.string().describe('The element that morphs (it keeps its id through every state).'),
4106
+ states: z.array(z.object({
4107
+ at: z.number().min(0).describe('Seconds: when the morph INTO this state starts.'),
4108
+ duration: z.number().positive().optional().describe('Seconds the morph takes (default 0.8).'),
4109
+ to: z.string().optional().describe('An existing item whose shape this state takes (it is hidden; it is only the shape reference).'),
4110
+ shape: z.object({
4111
+ itemType: z.string(),
4112
+ position: z.object({ x: z.number(), y: z.number() }),
4113
+ properties: z.record(z.string(), z.unknown()).optional(),
4114
+ }).optional().describe('Or describe the state: it is created hidden as the reference.'),
4115
+ }).refine((s) => !!s.to !== !!s.shape, { message: 'each state takes exactly one of to or shape' })).min(1).max(40),
4116
+ easing: z.enum(['linear', 'easeIn', 'easeOut', 'easeInOut']).optional().describe('Morph easing (default easeInOut) — the morph supports these four.'),
4117
+ camera: z.union([z.boolean(), z.object({ fill: z.number().min(0.05).max(1).optional(), easing: z.string().optional() })]).optional()
4118
+ .describe('true: the camera rides every morph on the same timing, framing each state (fill = share of the frame the state fills, default 0.45).'),
4119
+ sound: z.union([z.boolean(), z.string().min(1)]).optional().describe("true: a whoosh at each morph start; or a catalogue sound name."),
4120
+ }).superRefine((v, ctx) => {
4121
+ let end = -Infinity;
4122
+ [...v.states].sort((a, b) => a.at - b.at).forEach((s, i) => {
4123
+ if (s.at < end - 1e-6)
4124
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: `state at ${s.at} s starts before the previous morph ends at ${end} s`, path: ['states', i, 'at'] });
4125
+ end = s.at + (s.duration ?? 0.8);
4126
+ });
4127
+ });
4128
+ /** pinepaper_render_hook — per-frame drawing saved as SOURCE (FxTool D29). */
4129
+ export const RenderHookInputSchema = z.object({
4130
+ action: z.enum(['register', 'unregister', 'list']),
4131
+ id: z.string().min(1).optional(),
4132
+ source: z.string().min(1).optional().describe('register: JS that evaluates to (ctx, t, info) => {}.'),
4133
+ layer: z.enum(['above', 'below']).optional(),
4134
+ deterministic: z.boolean().optional(),
4135
+ seed: z.number().int().optional(),
4136
+ }).refine((v) => v.action === 'list' || !!v.id, { message: 'register / unregister require id', path: ['id'] })
4137
+ .refine((v) => v.action !== 'register' || !!v.source, { message: 'register requires source', path: ['source'] });
4138
+ /** pinepaper_choreograph — beat verbs compiled to one squash-and-stretch track (D67). */
4139
+ const ChoreoPointSchema = z.union([z.tuple([z.number(), z.number()]), z.object({ x: z.number(), y: z.number() })]);
4140
+ const ChoreoEdgeSchema = z.enum(['left', 'right', 'top', 'bottom']);
4141
+ export const CHOREO_VERBS = ['pop', 'drop', 'hop', 'bounce', 'roll', 'fly', 'peek', 'shake', 'squash', 'enter', 'exit'];
4142
+ export const ChoreographBeatSchema = z.object({
4143
+ at: z.number().min(0).describe('Seconds. Beats run one after another; one that starts before the previous ends is refused.'),
4144
+ verb: z.enum(CHOREO_VERBS),
4145
+ to: ChoreoPointSchema.optional().describe('Where the actor ends up (its centre, canvas px): hop / bounce / roll / fly / drop / enter. roll and fly require it.'),
4146
+ duration: z.number().positive().optional().describe('Seconds for this beat (each verb has a sensible default).'),
4147
+ height: z.number().positive().optional().describe('hop / bounce: jump height px (default 1.2× / 1.5× the actor).'),
4148
+ times: z.number().int().min(1).max(12).optional().describe('bounce: number of bounces (default 3).'),
4149
+ decay: z.number().min(0.1).max(0.95).optional().describe('bounce: each bounce is this fraction of the last (default 0.6).'),
4150
+ arc: z.number().optional().describe('fly: how far above the higher end the path rises, px (default 2× the actor).'),
4151
+ edge: ChoreoEdgeSchema.optional().describe('peek: the frame edge it peeks from (default left). exit: the edge it leaves by (default right).'),
4152
+ from: ChoreoEdgeSchema.optional().describe('enter: the frame edge it comes in from (default left).'),
4153
+ amount: z.number().min(0).max(1).optional().describe('peek: how much of it shows, 0..1 (default 0.5). squash: depth (default 0.25).'),
4154
+ hold: z.number().min(0).optional().describe('peek: seconds it stays out (default 1).'),
4155
+ intensity: z.number().positive().optional().describe('shake: px each way (default 8% of its width).'),
4156
+ }).strict().superRefine((b, ctx) => {
4157
+ if ((b.verb === 'roll' || b.verb === 'fly') && !b.to)
4158
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: `${b.verb} needs to: [x, y]`, path: ['to'] });
4159
+ if (b.verb === 'exit' && b.to !== undefined)
4160
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'exit leaves by an edge: pass edge (left | right | top | bottom), not to', path: ['to'] });
4161
+ });
4162
+ const ChoreoSoundName = z.union([z.string().min(1), z.literal(false)]);
4163
+ export const ChoreographInputSchema = z.object({
4164
+ itemId: z.string().describe('The actor: any item — a shape, a group, an imported SVG character, a photo cut-out.'),
4165
+ beats: z.array(ChoreographBeatSchema).min(1).max(60),
4166
+ sound: z.union([z.boolean(), z.object({
4167
+ pop: ChoreoSoundName.optional(), whoosh: ChoreoSoundName.optional(),
4168
+ contact: ChoreoSoundName.optional(), shake: ChoreoSoundName.optional(),
4169
+ gain: z.number().min(0).max(1).optional(),
4170
+ }).strict()]).optional().describe("true: a sound on every pop, launch, landing and shake, its loudness following the motion (a bounce's contacts get quieter as it decays). An object picks the sound per moment by catalogue name (an SFX — beep, pop, wind, whoosh, zap — or a drum — kick, snare, tom, hat, clap — or one you defined), false to leave one silent, and gain 0..1. Placed on the timeline, baked into video and render_soundtrack."),
4171
+ });
3955
4172
  /**
3956
4173
  * pinepaper_shader_graph — validated JSON pixel graphs (FxTool D5), CPU-backed.
3957
4174
  * Linear chains only; the engine names every error (unknown type, duplicate,
@@ -4322,6 +4539,7 @@ export const StoryInputSchema = z.object({
4322
4539
  spec: z.record(z.string(), z.unknown()).optional().describe('apply_spec: the story spec — usually one from `distill`, edited.'),
4323
4540
  images: z.array(z.union([z.string(), z.record(z.string(), z.unknown())])).optional().describe('plan_book: the images to lay out, as ids or specs.'),
4324
4541
  options: z.record(z.string(), z.unknown()).optional().describe('Assembly or layout options, passed through.'),
4542
+ nav: z.enum(['panels', 'scenes']).optional().describe("from_text / apply_spec: 'scenes' is the VIDEO form — one scene per part on a scene chain, cut in time (export it as MP4); 'panels' (default) the WIDGET form — tabs a viewer clicks through (export_widget_html)."),
4325
4543
  })
4326
4544
  .refine((v) => !['distill', 'from_text'].includes(v.action) || !!v.text, { message: 'this action requires text', path: ['text'] })
4327
4545
  .refine((v) => v.action !== 'apply_spec' || !!v.spec, { message: 'apply_spec requires spec', path: ['spec'] })
@@ -4659,4 +4877,88 @@ export const RiggingInputSchema = z.object({
4659
4877
  .refine((v) => !['import_bvh', 'retarget_bvh'].includes(v.action) || !!v.bvhText, { message: 'import_bvh/retarget_bvh require bvhText (the .bvh file contents)', path: ['bvhText'] })
4660
4878
  .refine((v) => v.action !== 'retarget_bvh' || v.skeletonId != null, { message: 'retarget_bvh requires skeletonId (the existing rig to drive)', path: ['skeletonId'] })
4661
4879
  .refine((v) => v.action !== 'import_spine' || !!v.spineJson, { message: 'import_spine requires spineJson', path: ['spineJson'] });
4880
+ // =============================================================================
4881
+ // CRAFT TOOLS SHARED WITH THE CLOUD (1.6.21): weave, knots, sashiko,
4882
+ // subdivision, UI kit. The JSON schemas are mcp-cloud's, vendored by
4883
+ // scripts/sync-design-systems.mjs; schema-parity.test.ts holds these to them.
4884
+ // The generators write colours into the script inside quotes and numbers bare,
4885
+ // so every value is checked here, before it becomes code.
4886
+ // =============================================================================
4887
+ const CRAFT_COLOUR = /^(#[0-9a-f]{3,8}|rgba?\([\d\s.,%]+\)|hsla?\([\d\s.,%deg]+\)|[a-z]{3,20})$/i;
4888
+ const CraftColour = z.string().trim().regex(CRAFT_COLOUR, 'a colour: #rrggbb, rgb(…), hsl(…) or a CSS name');
4889
+ const CraftNumber = z.number().finite();
4890
+ const CraftBounds = z.object({
4891
+ x: CraftNumber.optional(),
4892
+ y: CraftNumber.optional(),
4893
+ width: z.number().finite().positive().optional(),
4894
+ height: z.number().finite().positive().optional(),
4895
+ }).describe('Rectangle to fill. Default: the canvas.');
4896
+ export const DrawWeaveInputSchema = z.object({
4897
+ weave: z.enum(['plain', 'twill', 'basket', 'herringbone']).optional(),
4898
+ warpColor: CraftColour.optional(),
4899
+ weftColor: CraftColour.optional(),
4900
+ threadThickness: z.number().finite().positive().max(64).optional(),
4901
+ density: z.number().finite().min(4).max(400).optional(),
4902
+ floatRatio: z.number().finite().min(1).max(16).optional(),
4903
+ twillVariant: z.enum(['standard', 'herringbone']).optional(),
4904
+ chevronWidth: z.number().finite().positive().max(4000).optional(),
4905
+ bundleSize: z.number().int().min(1).max(16).optional(),
4906
+ bundleGap: z.number().finite().min(0).max(200).optional(),
4907
+ slubJitter: z.number().finite().min(0).max(4).optional(),
4908
+ opacity: z.number().finite().min(0).max(1).optional(),
4909
+ bounds: CraftBounds.optional(),
4910
+ });
4911
+ const KnotPoint = z.object({
4912
+ x: CraftNumber,
4913
+ y: CraftNumber,
4914
+ radius: z.number().finite().positive().max(500).optional(),
4915
+ color: CraftColour.optional(),
4916
+ highlightColor: CraftColour.optional(),
4917
+ shadowColor: CraftColour.optional(),
4918
+ length: z.number().finite().positive().max(2000).optional(),
4919
+ angle: CraftNumber.optional(),
4920
+ });
4921
+ export const DrawKnotsInputSchema = z.object({
4922
+ knotType: z.enum(['french_knot', 'bullion_roll', 'knot_anchor']).optional(),
4923
+ points: z.array(KnotPoint).max(2000).optional(),
4924
+ threadColor: CraftColour.optional(),
4925
+ highlightColor: CraftColour.optional(),
4926
+ shadowColor: CraftColour.optional(),
4927
+ defaultRadius: z.number().finite().positive().max(500).optional(),
4928
+ });
4929
+ export const DrawSashikoInputSchema = z.object({
4930
+ pattern: z.enum(['seigaiha', 'asanoha', 'hishi', 'kaki_no_hana']).optional(),
4931
+ stitchLength: z.number().finite().positive().max(200).optional(),
4932
+ gapLength: z.number().finite().min(0).max(200).optional(),
4933
+ vertexGap: z.number().finite().min(0).max(200).optional(),
4934
+ scale: z.number().finite().min(8).max(1000).optional(),
4935
+ concentricArcs: z.number().int().min(1).max(12).optional(),
4936
+ threadColor: CraftColour.optional(),
4937
+ groundColor: CraftColour.optional(),
4938
+ threadWidth: z.number().finite().positive().max(64).optional(),
4939
+ bounds: CraftBounds.optional(),
4940
+ });
4941
+ export const DrawSubdivisionInputSchema = z.object({
4942
+ preset: z.enum(['mondrian', 'swiss', 'bauhaus', 'minimal', 'custom']).optional(),
4943
+ maxDepth: z.number().int().min(1).max(8).optional(),
4944
+ minDepth: z.number().int().min(0).max(8).optional(),
4945
+ splitBias: z.array(z.number().finite().min(0.05).max(0.95)).length(2).optional(),
4946
+ colorProbability: z.number().finite().min(0).max(1).optional(),
4947
+ nestedProbability: z.number().finite().min(0).max(1).optional(),
4948
+ dividerWidth: z.number().finite().min(0).max(200).optional(),
4949
+ dividerColor: CraftColour.optional(),
4950
+ groundColor: CraftColour.optional(),
4951
+ accentColors: z.array(CraftColour).max(16).optional(),
4952
+ cellInset: z.number().finite().min(0).max(200).optional(),
4953
+ bounds: CraftBounds.optional(),
4954
+ seed: z.number().int().optional(),
4955
+ idPrefix: z.string().regex(/^[A-Za-z][\w-]{0,40}$/, 'letters, digits, _ and -').optional(),
4956
+ });
4957
+ /**
4958
+ * The UI kit's own lowering (vendor/design/ui-kit-tool-schema.ts) whitelists,
4959
+ * types and JSON-encodes every argument, so only the action is checked here.
4960
+ */
4961
+ export const UiKitInputSchema = z.object({
4962
+ action: z.enum(['phone_lock_screen', 'notification', 'prompt_box', 'toggle', 'ad_card', 'calendar_card']),
4963
+ }).passthrough();
4662
4964
  //# sourceMappingURL=schemas.js.map