@pinepaper.studio/mcp-server 1.5.5 → 1.6.1
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 +71 -0
- package/README.es.md +71 -0
- package/README.fr.md +71 -0
- package/README.hi.md +71 -0
- package/README.ja.md +71 -0
- package/README.ko.md +71 -0
- package/README.md +513 -272
- package/README.pt-BR.md +71 -0
- package/README.zh-CN.md +71 -0
- package/assets/easing-splashes.svg +152 -0
- package/assets/equation-path.svg +77 -0
- package/assets/hero.svg +126 -0
- package/assets/live-badge.svg +62 -0
- package/assets/poster-sunburst.svg +106 -0
- package/dist/__tests__/fixtures/mock-relations.d.ts +48 -0
- package/dist/__tests__/fixtures/mock-relations.d.ts.map +1 -1
- package/dist/__tests__/fixtures/mock-relations.js +31 -0
- package/dist/__tests__/fixtures/mock-relations.js.map +1 -1
- package/dist/agent/types.d.ts +6 -6
- package/dist/browser/bridge.d.ts +48 -4
- package/dist/browser/bridge.d.ts.map +1 -1
- package/dist/browser/bridge.js +79 -18
- package/dist/browser/bridge.js.map +1 -1
- package/dist/browser/puppeteer-controller.d.ts +34 -0
- package/dist/browser/puppeteer-controller.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.js +116 -5
- package/dist/browser/puppeteer-controller.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/ontology/index.d.ts +2 -0
- package/dist/ontology/index.d.ts.map +1 -1
- package/dist/ontology/index.js +2 -0
- package/dist/ontology/index.js.map +1 -1
- package/dist/ontology/ontology-compiler.d.ts +78 -0
- package/dist/ontology/ontology-compiler.d.ts.map +1 -0
- package/dist/ontology/ontology-compiler.js +235 -0
- package/dist/ontology/ontology-compiler.js.map +1 -0
- package/dist/ontology/ontology-validator.d.ts +103 -0
- package/dist/ontology/ontology-validator.d.ts.map +1 -0
- package/dist/ontology/ontology-validator.js +573 -0
- package/dist/ontology/ontology-validator.js.map +1 -0
- package/dist/ontology/types.d.ts +1 -0
- package/dist/ontology/types.d.ts.map +1 -1
- package/dist/ontology/validator.d.ts +6 -0
- package/dist/ontology/validator.d.ts.map +1 -1
- package/dist/ontology/validator.js +34 -5
- package/dist/ontology/validator.js.map +1 -1
- package/dist/ontology/vocabulary.d.ts.map +1 -1
- package/dist/ontology/vocabulary.js +91 -3
- package/dist/ontology/vocabulary.js.map +1 -1
- package/dist/tools/definitions.d.ts +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +978 -27
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/handlers/maps.d.ts.map +1 -1
- package/dist/tools/handlers/maps.js +54 -1
- package/dist/tools/handlers/maps.js.map +1 -1
- package/dist/tools/handlers/ontology.d.ts.map +1 -1
- package/dist/tools/handlers/ontology.js +15 -3
- package/dist/tools/handlers/ontology.js.map +1 -1
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +137 -6
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +20 -1
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +25 -2
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +190 -2
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +1068 -30
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +2726 -116
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +594 -4
- package/dist/types/schemas.js.map +1 -1
- package/manifest.json +299 -71
- package/package.json +14 -3
package/dist/types/schemas.js
CHANGED
|
@@ -92,6 +92,12 @@ export const ItemTypeSchema = z.enum([
|
|
|
92
92
|
'path',
|
|
93
93
|
'line',
|
|
94
94
|
'arc',
|
|
95
|
+
// Named shape variants (regular polygons + paths) — createable directly by name
|
|
96
|
+
'pentagon',
|
|
97
|
+
'hexagon',
|
|
98
|
+
'diamond',
|
|
99
|
+
'arrow',
|
|
100
|
+
'heart',
|
|
95
101
|
]);
|
|
96
102
|
// Text item properties
|
|
97
103
|
export const TextPropertiesSchema = z.object({
|
|
@@ -217,6 +223,40 @@ export const RelationTypeSchema = z.enum([
|
|
|
217
223
|
'group_morphs_to',
|
|
218
224
|
// Self-relation; item moves along a custom-drawn path stored in params
|
|
219
225
|
'moves_along_path',
|
|
226
|
+
// Geometric construction constraints (Layer 2) — the source is RE-DERIVED each
|
|
227
|
+
// frame from its anchor item(s), so dragging an anchor updates it live
|
|
228
|
+
// (GeoGebra-style). Anchor A is the relation target; extra anchors ride in params.
|
|
229
|
+
'is_midpoint_of', // source = midpoint(target, params.other)
|
|
230
|
+
'lies_on_line', // source on line target→params.other at fraction params.t
|
|
231
|
+
'is_centroid_of', // source = centroid(target, ...params.others)
|
|
232
|
+
'is_circumcenter_of', // source = circumcenter(target, params.other1, params.other2)
|
|
233
|
+
'concentric_with', // source shares the target's center
|
|
234
|
+
'construction_reveal', // self-relation: opacity 0→1 at params.revealAt over params.fadeIn (timeline-driven)
|
|
235
|
+
// Procedural / deterministic property binding (Expression IR — FxTool S10 G1).
|
|
236
|
+
// params.signal:true routes through the pure, seed/frame-deterministic signal
|
|
237
|
+
// interpreter (replay-stable), else the per-frame compute fallback runs.
|
|
238
|
+
'driven_by', // source property = target property * multiplier + offset (+clamp); drives color/stroke/opacity/scale/pos. Needs a target.
|
|
239
|
+
'time_expression', // self-relation: source property driven by a math expression f(t, v) — t=time, v=params.baseValue
|
|
240
|
+
// Event-driven scene chains (FxTool S11). The source is a pp:event (make one
|
|
241
|
+
// with pinepaper_event); firing it runs these reactions. on_event_fire_after
|
|
242
|
+
// chains events over canvas/wall time; on_event_add_relation mutates the graph
|
|
243
|
+
// (the scene evolves itself). Kick a chain off with pinepaper_event pulse.
|
|
244
|
+
'on_event_fire_after', // event→event: when source fires, pulse target after params.delay (timeline: wall|canvas)
|
|
245
|
+
'on_event_add_relation', // event→item: add params.type relation from item to params.target when the event fires
|
|
246
|
+
'on_event_remove_relation', // event→item: remove params.type relation from item to params.target
|
|
247
|
+
'on_event_set_color', // event→item: set fill/stroke to params.color
|
|
248
|
+
'on_event_set_property', // event→item: set item[params.property] = params.value
|
|
249
|
+
'on_event_set_visibility', // event→item: show/hide (params.visible, default true)
|
|
250
|
+
// Structural layout relations (FxTool S12-E1) — STATIC composition as graph edges.
|
|
251
|
+
// Placement is derived from the TARGET'S BOUNDS and re-derived each frame, so
|
|
252
|
+
// dragging or resizing the target moves the dependent live. Use these instead of
|
|
253
|
+
// hardcoding x/y when the intent is "on", "under", "next to", "inside".
|
|
254
|
+
'on_top_of', // source bottom edge rests on target top edge (params: gap, align, overhang)
|
|
255
|
+
'below', // source top edge rests on target bottom edge (params: gap, align, overhang)
|
|
256
|
+
'beside', // source flanks the target left/right (params: side, gap, align)
|
|
257
|
+
'inside', // source anchored inside the target's bounds (params: anchor, padding)
|
|
258
|
+
'centered_on', // source center = target center + (offsetX, offsetY)
|
|
259
|
+
'aligned_with', // source matches target on ONE axis only — the other stays free (params: axis, offset)
|
|
220
260
|
]).describe('Type of relationship between items');
|
|
221
261
|
// Relation parameter schemas
|
|
222
262
|
export const OrbitsParamsSchema = z.object({
|
|
@@ -337,19 +377,207 @@ export const GroupMorphsToParamsSchema = z.object({
|
|
|
337
377
|
easing: z.enum(['linear', 'easeIn', 'easeOut', 'easeInOut']).optional().default('easeInOut').describe('Easing for the position interpolation'),
|
|
338
378
|
deformLines: z.boolean().optional().default(true).describe('Path.Line children deform via segment endpoints instead of rigid translation'),
|
|
339
379
|
});
|
|
380
|
+
// =============================================================================
|
|
381
|
+
// EQUATION-DRIVEN PATHS (Expression IR — FxTool S10 B5)
|
|
382
|
+
// Deterministic: math strings are parsed via expr-to-ir (no eval), sampled on
|
|
383
|
+
// the Expression IR. Shared by pinepaper_equation_path (geometry) and the
|
|
384
|
+
// moves_along_path `equation` motion source.
|
|
385
|
+
// =============================================================================
|
|
386
|
+
// Curated curve library (curvePreset). Each is a parametric preset.
|
|
387
|
+
export const CurvePresetSchema = z.enum(['spiral', 'rose', 'lissajous', 'astroid', 'heart', 'spirograph'])
|
|
388
|
+
.describe('Curated curve preset');
|
|
389
|
+
// One Fourier term for kind:"fourier".
|
|
390
|
+
export const HarmonicSchema = z.object({
|
|
391
|
+
freq: z.number().describe('Harmonic frequency'),
|
|
392
|
+
amp: z.number().describe('Amplitude'),
|
|
393
|
+
phase: z.number().optional().describe('Phase offset (radians)'),
|
|
394
|
+
});
|
|
395
|
+
// Chained parametric warp — displaces each sampled point by (dx, dy),
|
|
396
|
+
// expressions of x, y (point coords) and t (0..1 along the path).
|
|
397
|
+
export const EquationWarpSchema = z.object({
|
|
398
|
+
dx: z.string().optional().describe('x displacement expression of x, y, t (0..1)'),
|
|
399
|
+
dy: z.string().optional().describe('y displacement expression of x, y, t (0..1)'),
|
|
400
|
+
});
|
|
401
|
+
// The math-source fields common to both equation surfaces (no placement).
|
|
402
|
+
const equationSpecShape = {
|
|
403
|
+
kind: z.enum(['function', 'parametric', 'fourier', 'preset']).optional().default('function').describe("'function' y=f(variable) · 'parametric' x=fx(param),y=fy(param) · 'fourier' harmonics · 'preset' curated curve"),
|
|
404
|
+
expr: z.string().optional().describe("y = f(variable) — kind 'function', e.g. 'sin(x)*80'"),
|
|
405
|
+
xExpr: z.string().optional().describe("x = fx(param) — kind 'parametric'"),
|
|
406
|
+
yExpr: z.string().optional().describe("y = fy(param) — kind 'parametric'"),
|
|
407
|
+
harmonics: z.array(HarmonicSchema).optional().describe("Fourier terms — kind 'fourier'"),
|
|
408
|
+
preset: CurvePresetSchema.optional().describe("Curated curve — kind 'preset'"),
|
|
409
|
+
variable: z.string().optional().default('x').describe("Sample variable for kind 'function'"),
|
|
410
|
+
param: z.string().optional().default('t').describe("Sample variable for kind 'parametric'/'preset'"),
|
|
411
|
+
min: z.number().optional().describe('Sample range start'),
|
|
412
|
+
max: z.number().optional().describe('Sample range end'),
|
|
413
|
+
samples: z.number().optional().default(200).describe('Number of samples along the range'),
|
|
414
|
+
scale: z.number().optional().default(1).describe('Multiply equation coords before placing'),
|
|
415
|
+
flipY: z.boolean().optional().default(true).describe('Math y-up → screen y-down'),
|
|
416
|
+
warp: z.union([EquationWarpSchema, z.array(EquationWarpSchema)]).optional().describe('Chained parametric warp(s) applied to the sampled points'),
|
|
417
|
+
};
|
|
340
418
|
// Self-relation: item is driven along a custom-drawn path stored in params.
|
|
341
419
|
// In the editor the Relations picker offers a drag-to-draw capture mode; via
|
|
342
420
|
// MCP / agent code, pass the points array explicitly.
|
|
421
|
+
// Equation motion source for moves_along_path (S10 B5): the item TRAVERSES a
|
|
422
|
+
// math curve instead of a drawn point list. Origin is cx/cy (default 0) —
|
|
423
|
+
// distinct from pinepaper_equation_path's x/y (canvas-center default).
|
|
424
|
+
// .passthrough(): preset tuning params (e.g. rose's k/r, spiral's turns) ride on
|
|
425
|
+
// the top-level object — FxTool does curvePreset(eq.preset, eq) — so they must
|
|
426
|
+
// survive validation instead of being stripped.
|
|
427
|
+
export const MovesAlongPathEquationSchema = z.object({
|
|
428
|
+
...equationSpecShape,
|
|
429
|
+
cx: z.number().optional().describe('Canvas origin x for the sampled curve (default 0)'),
|
|
430
|
+
cy: z.number().optional().describe('Canvas origin y for the sampled curve (default 0)'),
|
|
431
|
+
}).passthrough();
|
|
343
432
|
export const MovesAlongPathParamsSchema = z.object({
|
|
433
|
+
// path OR equation — one is required. equation overrides path when both are set.
|
|
344
434
|
path: z.array(z.union([
|
|
345
435
|
z.object({ x: z.number(), y: z.number() }),
|
|
346
436
|
z.tuple([z.number(), z.number()]),
|
|
347
|
-
])).describe('Path points: array of {x,y} objects or [x,y] tuples'),
|
|
437
|
+
])).optional().describe('Path points: array of {x,y} objects or [x,y] tuples (omit when using `equation`)'),
|
|
438
|
+
equation: MovesAlongPathEquationSchema.optional().describe('Equation motion source (S10 B5): traverse a math curve instead of a drawn path — deterministic, sampled on the Expression IR'),
|
|
348
439
|
speed: z.number().optional().default(1).describe('Speed multiplier (1 ≈ 150 px/s)'),
|
|
349
440
|
closed: z.boolean().optional().default(true).describe('Loop back to start at end of path'),
|
|
350
441
|
phase: z.number().optional().default(0).describe('Starting position along path (0–1)'),
|
|
351
442
|
easing: z.enum(['linear', 'easeIn', 'easeOut', 'easeInOut', 'sine', 'bounce', 'pingpong']).optional().default('linear').describe('Motion curve mapping normalized time → progress along path'),
|
|
352
443
|
});
|
|
444
|
+
// pinepaper_equation_path input — creates a path ITEM from a math equation.
|
|
445
|
+
// Placement is x/y (canvas center default) + style, on top of the shared spec.
|
|
446
|
+
// .passthrough(): preset tuning params (rose's k/r, spiral's turns, …) ride on
|
|
447
|
+
// the top-level object — FxTool does curvePreset(opts.preset, opts.presetParams
|
|
448
|
+
// || opts) — so they must survive validation.
|
|
449
|
+
export const EquationPathInputSchema = z.object({
|
|
450
|
+
...equationSpecShape,
|
|
451
|
+
x: z.number().optional().describe('Canvas origin x (default: canvas center)'),
|
|
452
|
+
y: z.number().optional().describe('Canvas origin y (default: canvas center)'),
|
|
453
|
+
style: z.record(z.unknown()).optional().describe('Path style forwarded to create (strokeColor, strokeWidth, fillColor, …)'),
|
|
454
|
+
}).passthrough();
|
|
455
|
+
// Procedural / deterministic property binding (Expression IR — FxTool S10 G1).
|
|
456
|
+
// driven_by: source's `sourceProperty` = target's `targetProperty` * multiplier
|
|
457
|
+
// + offset (optionally clamped). For fillColor/strokeColor the driven value
|
|
458
|
+
// (clamped 0..1) interpolates colorFrom→colorTo — a relation that drives COLOR.
|
|
459
|
+
// signal:true evaluates via the pure IR signal interpreter (frame-deterministic,
|
|
460
|
+
// x/y target props track a moving target); else the per-frame compute runs.
|
|
461
|
+
export const DrivenByParamsSchema = z.object({
|
|
462
|
+
sourceProperty: z.enum(['x', 'y', 'rotation', 'opacity', 'scale', 'strokeWidth', 'fillColor', 'strokeColor']).optional().default('opacity').describe('Property on the SOURCE item to drive'),
|
|
463
|
+
targetProperty: z.enum(['x', 'y', 'rotation', 'opacity', 'scale']).optional().default('x').describe('Property on the TARGET item to read'),
|
|
464
|
+
multiplier: z.number().optional().default(1).describe('Scale factor applied to the target value'),
|
|
465
|
+
offset: z.number().optional().default(0).describe('Offset added after the multiplier'),
|
|
466
|
+
clamp: z.object({ min: z.number().optional(), max: z.number().optional() }).optional().describe('Optional {min, max} clamp on the result'),
|
|
467
|
+
colorFrom: z.string().optional().default('#000000').describe('Color at driven value 0 (fillColor/strokeColor sourceProperty)'),
|
|
468
|
+
colorTo: z.string().optional().default('#ffffff').describe('Color at driven value 1 (fillColor/strokeColor sourceProperty)'),
|
|
469
|
+
signal: z.boolean().optional().default(false).describe('Deterministic IR-signal mode — replay-stable; x/y target props track a moving target'),
|
|
470
|
+
});
|
|
471
|
+
// time_expression: self-relation (targetId=null). The source's `property` is
|
|
472
|
+
// driven by a math expression of t (scene time) and v (baseValue). signal:true
|
|
473
|
+
// parses the expression into the Expression IR and evaluates a pure f(t)
|
|
474
|
+
// (replay-stable), falling back to per-frame if it uses random()/unknown symbols.
|
|
475
|
+
export const TimeExpressionParamsSchema = z.object({
|
|
476
|
+
property: z.string().optional().default('y').describe('Property to drive: x, y, rotation, opacity, scale'),
|
|
477
|
+
expression: z.string().optional().default('sin(t * 2) * 50 + 300').describe('Math expression of t (time) and v (baseValue), e.g. "sin(t*2)*50 + v"'),
|
|
478
|
+
baseValue: z.number().optional().default(0).describe('Base value accessible as v in the expression'),
|
|
479
|
+
signal: z.boolean().optional().default(false).describe('Deterministic IR mode — parse expression to IR, evaluate as pure f(t)'),
|
|
480
|
+
});
|
|
481
|
+
// =============================================================================
|
|
482
|
+
// EVENT-DRIVEN SCENE CHAINS (FxTool S11). Source of each is a pp:event.
|
|
483
|
+
// =============================================================================
|
|
484
|
+
// on_event_fire_after (event→event): chain a delayed pulse. timeline 'canvas'
|
|
485
|
+
// schedules on the animation clock (pauses/seeks/loops with the timeline) — use
|
|
486
|
+
// it for scene chains; 'wall' is real-world setTimeout ms.
|
|
487
|
+
export const OnEventFireAfterParamsSchema = z.object({
|
|
488
|
+
delay: z.number().min(0).optional().default(1000).describe('Delay before pulsing the target event (milliseconds)'),
|
|
489
|
+
timeline: z.enum(['wall', 'canvas']).optional().default('wall').describe("'canvas' = animation-clock time (scrub/loop-stable) · 'wall' = real setTimeout ms"),
|
|
490
|
+
});
|
|
491
|
+
// on_event_add_relation (event→item): when the event fires, add a relation FROM
|
|
492
|
+
// the target item TO params.target of type params.type. The scene mutates itself.
|
|
493
|
+
export const OnEventAddRelationParamsSchema = z.object({
|
|
494
|
+
type: z.string().describe('Relation type to add (e.g. orbits, moves_along_path, driven_by)'),
|
|
495
|
+
target: z.string().nullish().describe('Target item id for the added relation (null for self-relations)'),
|
|
496
|
+
params: z.record(z.unknown()).optional().default({}).describe('Params for the added relation'),
|
|
497
|
+
});
|
|
498
|
+
// on_event_remove_relation (event→item): tear down a relation added earlier.
|
|
499
|
+
export const OnEventRemoveRelationParamsSchema = z.object({
|
|
500
|
+
type: z.string().describe('Relation type to remove'),
|
|
501
|
+
target: z.string().nullish().describe('Target item id (null for self-relations)'),
|
|
502
|
+
});
|
|
503
|
+
// on_event_set_color (event→item).
|
|
504
|
+
export const OnEventSetColorParamsSchema = z.object({
|
|
505
|
+
color: z.string().describe('Color to apply when the event fires'),
|
|
506
|
+
which: z.enum(['fill', 'stroke']).optional().default('fill').describe('Which color channel to set'),
|
|
507
|
+
});
|
|
508
|
+
// on_event_set_property (event→item): set an arbitrary property.
|
|
509
|
+
export const OnEventSetPropertyParamsSchema = z.object({
|
|
510
|
+
property: z.string().describe('Item property to set (e.g. opacity, rotation, strokeWidth)'),
|
|
511
|
+
value: z.unknown().describe('Value to assign'),
|
|
512
|
+
});
|
|
513
|
+
// on_event_set_visibility (event→item).
|
|
514
|
+
export const OnEventSetVisibilityParamsSchema = z.object({
|
|
515
|
+
visible: z.boolean().optional().default(true).describe('Show (true) or hide (false) the item'),
|
|
516
|
+
});
|
|
517
|
+
// pinepaper_event tool — manage the pp:event channel.
|
|
518
|
+
// create → app.createEvent(name, {payloadType,x,y}) → returns eventId
|
|
519
|
+
// pulse → app.pulseEvent(eventId, payload) → kicks off a chain
|
|
520
|
+
export const EventInputSchema = z.object({
|
|
521
|
+
action: z.enum(['create', 'pulse']).describe("'create' a pp:event · 'pulse' it to fire listeners/chains"),
|
|
522
|
+
name: z.string().optional().describe("Event name — required for action 'create'"),
|
|
523
|
+
payloadType: z.string().optional().describe("Payload type label (default 'Pulse') — action 'create'"),
|
|
524
|
+
x: z.number().optional().describe('Canvas x for the event marker — action create'),
|
|
525
|
+
y: z.number().optional().describe('Canvas y for the event marker — action create'),
|
|
526
|
+
eventId: z.string().optional().describe("Event id to fire — required for action 'pulse'"),
|
|
527
|
+
payload: z.unknown().optional().describe("Optional value forwarded to listeners — action 'pulse'"),
|
|
528
|
+
}).refine((v) => v.action !== 'create' || !!v.name, { message: "action 'create' requires name", path: ['name'] })
|
|
529
|
+
.refine((v) => v.action !== 'pulse' || !!v.eventId, { message: "action 'pulse' requires eventId", path: ['eventId'] });
|
|
530
|
+
// =============================================================================
|
|
531
|
+
// STRUCTURAL LAYOUT RELATIONS (FxTool S12-E1)
|
|
532
|
+
// =============================================================================
|
|
533
|
+
// Placement expressed as a graph edge rather than baked-in coordinates. Every
|
|
534
|
+
// compute reads the TARGET'S bounds and the SOURCE'S own bounds, and re-runs each
|
|
535
|
+
// frame — move or resize the target and the dependent follows. All six need a
|
|
536
|
+
// target. `signal: true` routes through the pure Expression-IR port, which FxTool
|
|
537
|
+
// keeps bit-identical to the live compute.
|
|
538
|
+
// on_top_of: source bottom edge rests on the target's top edge.
|
|
539
|
+
// y = T.y - target.height/2 - gap - source.height/2
|
|
540
|
+
export const OnTopOfParamsSchema = z.object({
|
|
541
|
+
gap: z.number().optional().default(0).describe('Pixels between the two edges'),
|
|
542
|
+
align: z.enum(['left', 'center', 'right']).optional().default('center').describe('Horizontal alignment relative to the target'),
|
|
543
|
+
overhang: z.number().optional().default(0).describe('Extra signed x-offset in px'),
|
|
544
|
+
signal: z.boolean().optional().default(false).describe('Deterministic mode — route through the pure Expression-IR port'),
|
|
545
|
+
});
|
|
546
|
+
// below: the mirror of on_top_of — source top edge rests on the target's bottom edge.
|
|
547
|
+
export const BelowParamsSchema = z.object({
|
|
548
|
+
gap: z.number().optional().default(0).describe('Pixels between the two edges'),
|
|
549
|
+
align: z.enum(['left', 'center', 'right']).optional().default('center').describe('Horizontal alignment relative to the target'),
|
|
550
|
+
overhang: z.number().optional().default(0).describe('Extra signed x-offset in px'),
|
|
551
|
+
signal: z.boolean().optional().default(false).describe('Deterministic mode — route through the pure Expression-IR port'),
|
|
552
|
+
});
|
|
553
|
+
// beside: source flanks the target horizontally, aligned vertically.
|
|
554
|
+
export const BesideParamsSchema = z.object({
|
|
555
|
+
side: z.enum(['left', 'right']).optional().default('right').describe('Which side of the target'),
|
|
556
|
+
gap: z.number().optional().default(0).describe('Pixels between the two edges'),
|
|
557
|
+
align: z.enum(['top', 'center', 'bottom']).optional().default('center').describe('Vertical alignment relative to the target'),
|
|
558
|
+
signal: z.boolean().optional().default(false).describe('Deterministic mode — route through the pure Expression-IR port'),
|
|
559
|
+
});
|
|
560
|
+
// inside: source placed fully within the target's bounds at a 9-way anchor.
|
|
561
|
+
export const InsideParamsSchema = z.object({
|
|
562
|
+
anchor: z.enum(['center', 'top-left', 'top', 'top-right', 'left', 'right', 'bottom-left', 'bottom', 'bottom-right'])
|
|
563
|
+
.optional().default('center').describe('Where inside the target to anchor the source'),
|
|
564
|
+
padding: z.number().optional().default(0).describe('Inset from the target edge in px'),
|
|
565
|
+
signal: z.boolean().optional().default(false).describe('Deterministic mode — route through the pure Expression-IR port'),
|
|
566
|
+
});
|
|
567
|
+
// centered_on: source center = target center + offset (concentric when offset is 0).
|
|
568
|
+
export const CenteredOnParamsSchema = z.object({
|
|
569
|
+
offsetX: z.number().optional().default(0).describe('Signed x-offset from the target center'),
|
|
570
|
+
offsetY: z.number().optional().default(0).describe('Signed y-offset from the target center'),
|
|
571
|
+
signal: z.boolean().optional().default(false).describe('Deterministic mode — route through the pure Expression-IR port'),
|
|
572
|
+
});
|
|
573
|
+
// aligned_with: ONE axis of the source center matches the target's; the other axis
|
|
574
|
+
// is left untouched (FxTool writes only the returned component). `axis` is required —
|
|
575
|
+
// FxTool's compute returns undefined (relation does nothing) without it.
|
|
576
|
+
export const AlignedWithParamsSchema = z.object({
|
|
577
|
+
axis: z.enum(['x', 'y']).describe('Which axis to align (required — the relation is inert without it)'),
|
|
578
|
+
offset: z.number().optional().default(0).describe('Signed offset along that axis'),
|
|
579
|
+
signal: z.boolean().optional().default(false).describe('Deterministic mode — route through the pure Expression-IR port'),
|
|
580
|
+
});
|
|
353
581
|
// =============================================================================
|
|
354
582
|
// CUSTOM RELATION REGISTRATION
|
|
355
583
|
// =============================================================================
|
|
@@ -439,6 +667,22 @@ export const GeneratorNameSchema = z.enum([
|
|
|
439
667
|
'drawSimulation',
|
|
440
668
|
'drawSpectrumAnalyzer',
|
|
441
669
|
'draw3DSurface',
|
|
670
|
+
// S6 seeded procedural generators (curated presets + Motion group)
|
|
671
|
+
'drawTruchet',
|
|
672
|
+
'drawHalftone',
|
|
673
|
+
'drawRibbons',
|
|
674
|
+
// GPU / GLSL math-art generators
|
|
675
|
+
'drawFormulaArt',
|
|
676
|
+
'drawParametricCollection',
|
|
677
|
+
'drawShaderArt',
|
|
678
|
+
'drawYeganehMountains',
|
|
679
|
+
// OKLCH seeded backgrounds + 3D parametric curve
|
|
680
|
+
'drawBlobs',
|
|
681
|
+
'drawLowPoly',
|
|
682
|
+
'drawPeaks',
|
|
683
|
+
'drawScatter',
|
|
684
|
+
'drawStackedWaves',
|
|
685
|
+
'draw3DParametricCurve',
|
|
442
686
|
]).describe('Background generator name');
|
|
443
687
|
// ---------------------------------------------------------------------------
|
|
444
688
|
// Generator parameter schemas — Phase 1 (enhanced existing)
|
|
@@ -607,7 +851,12 @@ export const EffectTypeSchema = z.enum([
|
|
|
607
851
|
'fireflies',
|
|
608
852
|
'shockwave',
|
|
609
853
|
'trail',
|
|
610
|
-
|
|
854
|
+
// Shader auras (WebGL2 silhouette-clipped) — routed to ItemAuraSystem by
|
|
855
|
+
// the same app.applyEffect entry point as the particle types.
|
|
856
|
+
'heatmap',
|
|
857
|
+
'liquid_metal',
|
|
858
|
+
'gem_smoke',
|
|
859
|
+
]).describe('Visual effect type (particle effects + shader auras)');
|
|
611
860
|
export const SparkleParamsSchema = z.object({
|
|
612
861
|
color: ColorSchema.optional().default('#fbbf24').describe('Sparkle color'),
|
|
613
862
|
speed: z.number().optional().default(1).describe('Animation speed'),
|
|
@@ -674,6 +923,8 @@ export const CreateItemInputSchema = z.object({
|
|
|
674
923
|
properties: z.record(z.unknown()).optional().default({}),
|
|
675
924
|
animationType: z.string().optional().describe('Inline animation to apply on creation. Loop presets (pulse, rotate, bounce, fade, wobble, slide, typewriter) or "keyframe" with keyframes array. Equivalent to a follow-up pinepaper_animate / pinepaper_keyframe_animate call.'),
|
|
676
925
|
animationSpeed: z.number().optional().describe('Speed multiplier for loop animations (default: 1.0). Ignored when animationType is "keyframe".'),
|
|
926
|
+
animationIntensity: z.number().optional().describe('Loop animation amplitude (0.1 = ±10%, default 0.15). Drives pulse/wobble/bounce/breathe amplitude; honored by SVG/SMIL/widget export.'),
|
|
927
|
+
animationDelay: z.number().optional().describe('Loop animation start delay in seconds.'),
|
|
677
928
|
keyframes: z.array(KeyframeSchema).optional().describe('Required when animationType is "keyframe". Inline keyframe array attached at creation.'),
|
|
678
929
|
});
|
|
679
930
|
// Light direction for 3D effects
|
|
@@ -719,7 +970,7 @@ export const DeleteItemInputSchema = z.object({
|
|
|
719
970
|
// Add Relation
|
|
720
971
|
export const AddRelationInputSchema = z.object({
|
|
721
972
|
sourceId: z.string().describe('Registry ID of the source item'),
|
|
722
|
-
targetId: z.string().
|
|
973
|
+
targetId: z.string().nullish().describe('Registry ID of the target item; null/omitted for self-relations (animates, moves_along_path, construction_reveal, time_expression)'),
|
|
723
974
|
relationType: RelationTypeSchema,
|
|
724
975
|
params: z.record(z.unknown()).optional().default({}),
|
|
725
976
|
});
|
|
@@ -740,6 +991,8 @@ export const AnimateItemInputSchema = z.object({
|
|
|
740
991
|
itemId: z.string().describe('Registry ID of the item'),
|
|
741
992
|
animationType: SimpleAnimationTypeSchema,
|
|
742
993
|
speed: z.number().optional().default(1).describe('Animation speed multiplier'),
|
|
994
|
+
intensity: z.number().optional().describe('Animation amplitude (animationIntensity): 0.1 = ±10%, 0.2 = ±20% (default 0.15). Drives pulse/wobble/bounce/breathe amplitude and is now honored by SVG/SMIL/widget export.'),
|
|
995
|
+
delay: z.number().optional().describe('Animation start delay in seconds (animationDelay).'),
|
|
743
996
|
});
|
|
744
997
|
// Keyframe Animate
|
|
745
998
|
export const KeyframeAnimateInputSchema = z.object({
|
|
@@ -748,13 +1001,27 @@ export const KeyframeAnimateInputSchema = z.object({
|
|
|
748
1001
|
duration: z.number().optional().describe('Total animation duration'),
|
|
749
1002
|
loop: z.boolean().optional().default(false),
|
|
750
1003
|
timeOffset: z.number().optional().describe('Shift the whole clip in canvas time (seconds). The animation only runs while canvas time is in [timeOffset, timeOffset + (clipOutPoint - clipInPoint)].'),
|
|
1004
|
+
timeUnits: z.enum(['seconds', 'ms']).optional().describe("Explicit units for keyframe times/duration. 'seconds' disables the legacy >100→ms auto-detect — REQUIRED for long-form timelines with second values over 100."),
|
|
751
1005
|
clipInPoint: z.number().optional().describe('Skip the first N seconds of keyframe data — clip head trim.'),
|
|
752
1006
|
clipOutPoint: z.number().optional().describe('Stop at N seconds into the keyframe data — clip tail trim. Default: lastKeyframeTime.'),
|
|
753
1007
|
});
|
|
754
1008
|
// Execute Generator
|
|
1009
|
+
//
|
|
1010
|
+
// Region (added FxTool c81781c): when set, the generator draws into a clipped
|
|
1011
|
+
// sub-group translated to (x,y) sized (width × height). Region runs imply
|
|
1012
|
+
// preserve (won't wipe the canvas) and re-running the same generator replaces
|
|
1013
|
+
// its prior region. Different generators' regions coexist. Invalid/zero-size
|
|
1014
|
+
// regions fall back to full-canvas FxTool-side.
|
|
1015
|
+
export const GeneratorRegionSchema = z.object({
|
|
1016
|
+
x: z.number().describe('Region top-left X in canvas coords'),
|
|
1017
|
+
y: z.number().describe('Region top-left Y in canvas coords'),
|
|
1018
|
+
width: z.number().positive().describe('Region width in pixels'),
|
|
1019
|
+
height: z.number().positive().describe('Region height in pixels'),
|
|
1020
|
+
}).describe('Optional sub-region {x, y, width, height} the generator draws into.');
|
|
755
1021
|
export const ExecuteGeneratorInputSchema = z.object({
|
|
756
1022
|
generatorName: GeneratorNameSchema,
|
|
757
1023
|
params: z.record(z.unknown()).optional().default({}),
|
|
1024
|
+
region: GeneratorRegionSchema.optional(),
|
|
758
1025
|
});
|
|
759
1026
|
// Apply Effect
|
|
760
1027
|
export const ApplyEffectInputSchema = z.object({
|
|
@@ -777,6 +1044,10 @@ export const PlayTimelineInputSchema = z.object({
|
|
|
777
1044
|
duration: z.number().optional().describe('Duration for play action'),
|
|
778
1045
|
loop: z.boolean().optional(),
|
|
779
1046
|
time: z.number().optional().describe('Time to seek to'),
|
|
1047
|
+
deterministic: z
|
|
1048
|
+
.boolean()
|
|
1049
|
+
.optional()
|
|
1050
|
+
.describe('For seek: evaluate the scene at the exact time via app.sceneAt(t) — ticks keyframes + relations + generators deterministically (same t → same frame), not just the keyframe state. Use before a screenshot for a reproducible frame.'),
|
|
780
1051
|
});
|
|
781
1052
|
// Canvas Control
|
|
782
1053
|
export const SetBackgroundColorInputSchema = z.object({
|
|
@@ -1333,7 +1604,7 @@ export const AgentResetInputSchema = z.object({
|
|
|
1333
1604
|
*/
|
|
1334
1605
|
export const AgentBatchOperationTypeSchema = z.enum([
|
|
1335
1606
|
'set_canvas_size', 'set_background', 'execute_generator',
|
|
1336
|
-
'create', 'modify', 'delete',
|
|
1607
|
+
'create', 'modify', 'delete', 'group',
|
|
1337
1608
|
'animate', 'keyframe_animate', 'relation',
|
|
1338
1609
|
'apply_mask', 'apply_effect',
|
|
1339
1610
|
'play_timeline',
|
|
@@ -1352,6 +1623,9 @@ export const AgentBatchOperationSchema = z.object({
|
|
|
1352
1623
|
properties: z.record(z.unknown()).optional().describe('Properties for create/modify operations'),
|
|
1353
1624
|
// Modify/Animate/Delete/Keyframe/Mask/Effect target
|
|
1354
1625
|
itemId: z.string().optional().describe('Target item ID or $N reference'),
|
|
1626
|
+
// Group operation fields
|
|
1627
|
+
itemIds: z.array(z.string()).optional().describe('Item IDs or $N references to combine for a "group" operation (the parts of a composite object).'),
|
|
1628
|
+
groupName: z.string().optional().describe('Optional name for the group created by a "group" operation (e.g. "car").'),
|
|
1355
1629
|
// Animate operation fields
|
|
1356
1630
|
animationType: z.string().optional().describe('Animation type for animate operations'),
|
|
1357
1631
|
animationOptions: z.record(z.unknown()).optional().describe('Animation options'),
|
|
@@ -1365,6 +1639,7 @@ export const AgentBatchOperationSchema = z.object({
|
|
|
1365
1639
|
// Execute generator fields
|
|
1366
1640
|
generatorName: z.string().optional().describe('Generator name for execute_generator'),
|
|
1367
1641
|
generatorParams: z.record(z.unknown()).optional().describe('Generator parameters'),
|
|
1642
|
+
generatorRegion: GeneratorRegionSchema.optional().describe('Optional sub-region {x, y, width, height} for execute_generator'),
|
|
1368
1643
|
// Set canvas size fields
|
|
1369
1644
|
width: z.number().min(100).max(4096).optional().describe('Canvas width for set_canvas_size (100-4096)'),
|
|
1370
1645
|
height: z.number().min(100).max(4096).optional().describe('Canvas height for set_canvas_size (100-4096)'),
|
|
@@ -1388,6 +1663,57 @@ export const AgentBatchOperationSchema = z.object({
|
|
|
1388
1663
|
action: z.string().optional().describe('Timeline action for play_timeline (play, stop, seek)'),
|
|
1389
1664
|
time: z.number().optional().describe('Seek time for play_timeline'),
|
|
1390
1665
|
}).describe('Single batch operation');
|
|
1666
|
+
// Group / ungroup — combine the parts of a composite object into ONE draggable entity
|
|
1667
|
+
// (a car's body + wheels + windows), or break a group back into loose items.
|
|
1668
|
+
export const GroupInputSchema = z.object({
|
|
1669
|
+
action: z.enum(['group', 'ungroup', 'break_apart']).describe('group: combine itemIds into one entity. ungroup: dissolve a group back into loose items. break_apart: split an imported SVG / group / compound-path into individually movable parts (re-grouped as one).'),
|
|
1670
|
+
itemIds: z.array(z.string()).optional().describe('For group: registry IDs of the items to combine.'),
|
|
1671
|
+
itemId: z.string().optional().describe('For break_apart: registry ID of the imported SVG / group / compound-path to decompose into parts.'),
|
|
1672
|
+
groupName: z.string().optional().describe('For group: optional name (e.g. "car", "robot").'),
|
|
1673
|
+
groupId: z.string().optional().describe('For ungroup: registry ID of the group to dissolve.'),
|
|
1674
|
+
});
|
|
1675
|
+
// Camera director — compile a high-level SHOT LIST into one camera_animates walkthrough
|
|
1676
|
+
// (FxTool's DirectorCompiler). 'auto' derives a shot per item; 'shots' takes an explicit list.
|
|
1677
|
+
export const CameraDirectorInputSchema = z.object({
|
|
1678
|
+
action: z.enum(['auto', 'shots']).describe("auto: generate a default walkthrough (one shot per item). shots: apply an explicit shot list."),
|
|
1679
|
+
shots: z
|
|
1680
|
+
.array(z.object({
|
|
1681
|
+
subjects: z.union([z.literal('everything'), z.array(z.string())]).describe("'everything' or item registry IDs the shot frames"),
|
|
1682
|
+
framing: z.union([z.enum(['tight', 'medium', 'wide']), z.number()]).optional().describe('Framing tightness (or an explicit zoom number). Default medium.'),
|
|
1683
|
+
hold: z.number().optional().describe('Seconds to dwell on the shot (default 1.2).'),
|
|
1684
|
+
moveIn: z.string().optional().describe("Move style, e.g. 'push-in' | 'pan'."),
|
|
1685
|
+
ease: z.enum(['smooth', 'linear', 'settle', 'snap']).optional().describe('Easing between shots (default smooth).'),
|
|
1686
|
+
transition: z.number().optional().describe('Seconds to travel into this shot (default 1.0).'),
|
|
1687
|
+
}))
|
|
1688
|
+
.optional()
|
|
1689
|
+
.describe('For action "shots": ordered shot list. Each shot frames its subjects at the given tightness.'),
|
|
1690
|
+
order: z.enum(['reading', 'reverse', 'creation']).optional().describe('For action "auto": item visiting order (default reading).'),
|
|
1691
|
+
hold: z.number().optional().describe('For action "auto": per-shot dwell seconds.'),
|
|
1692
|
+
establishing: z.boolean().optional().describe('For action "auto": open with a wide establishing shot of everything.'),
|
|
1693
|
+
loop: z.boolean().optional().describe('Loop the camera walkthrough.'),
|
|
1694
|
+
});
|
|
1695
|
+
// On-device object detection (FxTool ImageWorkflow / DETR·YOLO). Find objects in an
|
|
1696
|
+
// imported image; optionally promote each detection to a typed, image-anchored design
|
|
1697
|
+
// node (pp:Detected*, Wikidata-aliased) so relations can bind a shape to it.
|
|
1698
|
+
export const DetectObjectsInputSchema = z.object({
|
|
1699
|
+
itemId: z.string().optional().describe('Registry ID of the image to scan (default: the most recent image on the canvas).'),
|
|
1700
|
+
threshold: z.number().min(0).max(1).optional().describe('Confidence threshold 0-1 (default ~0.35 for DETR, ~0.1 for open-vocabulary).'),
|
|
1701
|
+
asNodes: z.boolean().optional().describe('true → promote each detection to a typed, image-anchored design NODE (a relation target: pp:Detected* aliased to a Wikidata entity) instead of drawing a labeled box. Enables relational compositing (bind a shape on/around/at the object; it tracks the image).'),
|
|
1702
|
+
queries: z.union([z.array(z.string()), z.string()]).optional().describe('OPEN-VOCABULARY detection (OWL-ViT, zero-shot): a list (or comma-separated string) of things to find by description — e.g. ["red umbrella","traffic light"]. Finds whatever you describe instead of the fixed 80 COCO classes. Omit to use the default DETR detector. The OWL-ViT model is a large one-time on-device download.'),
|
|
1703
|
+
});
|
|
1704
|
+
// Extract the best-matching detected region from an image as a NEW item.
|
|
1705
|
+
export const ExtractObjectInputSchema = z.object({
|
|
1706
|
+
label: z.string().optional().describe('What to extract (e.g. "cat", "person"). Picks the best-matching detection; default = highest-confidence.'),
|
|
1707
|
+
itemId: z.string().optional().describe('Registry ID of the source image (default: most recent image).'),
|
|
1708
|
+
x: z.number().optional().describe('Optional hint X (canvas coords) to disambiguate which detection to extract.'),
|
|
1709
|
+
y: z.number().optional().describe('Optional hint Y (canvas coords).'),
|
|
1710
|
+
threshold: z.number().min(0).max(1).optional().describe('Confidence threshold 0-1.'),
|
|
1711
|
+
});
|
|
1712
|
+
// Z-order / arrange — move an item up or down the stacking order.
|
|
1713
|
+
export const ArrangeInputSchema = z.object({
|
|
1714
|
+
itemId: z.string().describe('Registry ID of the item to reorder.'),
|
|
1715
|
+
action: z.enum(['front', 'back', 'forward', 'backward']).describe('front: bring to top of the stack. back: send to bottom. forward: move up one. backward: move down one.'),
|
|
1716
|
+
});
|
|
1391
1717
|
/**
|
|
1392
1718
|
* Batch execute input schema
|
|
1393
1719
|
*/
|
|
@@ -1545,6 +1871,7 @@ export const LetterCollageStyleSchema = z.enum([
|
|
|
1545
1871
|
'fold',
|
|
1546
1872
|
'gradient',
|
|
1547
1873
|
'image',
|
|
1874
|
+
'particle',
|
|
1548
1875
|
]).describe('Style type for letter collage');
|
|
1549
1876
|
export const TilePaletteSchema = z.enum([
|
|
1550
1877
|
// Game
|
|
@@ -1585,6 +1912,8 @@ export const CreateLetterCollageInputSchema = z.object({
|
|
|
1585
1912
|
gradientDirection: GradientDirectionSchema.optional().default('vertical').describe('Gradient direction (for style="gradient")'),
|
|
1586
1913
|
cornerRadius: z.number().optional().default(4).describe('Corner radius for tile backgrounds'),
|
|
1587
1914
|
shadowEnabled: z.boolean().optional().default(true).describe('Enable drop shadows'),
|
|
1915
|
+
particleSpacing: z.number().optional().describe('Dot spacing for style="particle" (smaller = denser cloud; default ~fontSize*0.11)'),
|
|
1916
|
+
particleDotSize: z.number().optional().describe('Base dot radius for style="particle" (default ~particleSpacing*0.44)'),
|
|
1588
1917
|
}).describe('Create letter collage input');
|
|
1589
1918
|
export const AnimateLetterCollageInputSchema = z.object({
|
|
1590
1919
|
collageId: z.string().describe('Collage ID from create_letter_collage'),
|
|
@@ -1740,6 +2069,54 @@ export const DeselectMapRegionsInputSchema = z.object({
|
|
|
1740
2069
|
}).describe('Deselect map regions input');
|
|
1741
2070
|
export const GetHighlightedMapRegionsInputSchema = z.object({}).describe('Get highlighted map regions input (no parameters)');
|
|
1742
2071
|
// =============================================================================
|
|
2072
|
+
// GLOBE + WORLD TOUR SCHEMAS
|
|
2073
|
+
// =============================================================================
|
|
2074
|
+
export const GlobeEnableInputSchema = z.object({
|
|
2075
|
+
rotation: z.tuple([z.number(), z.number(), z.number()]).optional().describe('Initial rotation angles [lambda, phi, gamma]'),
|
|
2076
|
+
momentum: z.boolean().optional().describe('Enable momentum/inertia on drag-spin'),
|
|
2077
|
+
showOcean: z.boolean().optional().describe('Render the ocean sphere backdrop'),
|
|
2078
|
+
}).describe('Enable globe (orthographic) mode input');
|
|
2079
|
+
export const GlobeRotateToInputSchema = z.object({
|
|
2080
|
+
lon: z.number().describe('Target longitude to rotate to face the viewer'),
|
|
2081
|
+
lat: z.number().describe('Target latitude to rotate to face the viewer'),
|
|
2082
|
+
duration: z.number().optional().describe('Animation duration in seconds (versor slerp)'),
|
|
2083
|
+
}).describe('Rotate globe to coordinate input');
|
|
2084
|
+
export const GlobeSpinInputSchema = z.object({
|
|
2085
|
+
speed: z.number().optional().describe('Rotation speed (degrees per second)'),
|
|
2086
|
+
axis: z.enum(['longitude', 'latitude']).optional().describe('Axis to spin around'),
|
|
2087
|
+
duration: z.number().optional().describe('Spin duration in seconds (omit for continuous)'),
|
|
2088
|
+
}).describe('Continuous globe spin input');
|
|
2089
|
+
export const WorldTourInputSchema = z.object({
|
|
2090
|
+
regions: z.array(z.string()).optional().describe('Region IDs to visit in order'),
|
|
2091
|
+
coords: z.array(z.tuple([z.number(), z.number()])).optional().describe('Coordinate stops [lon, lat][] to visit in order'),
|
|
2092
|
+
dwell: z.number().optional().describe('Seconds to dwell at each stop'),
|
|
2093
|
+
travel: z.number().optional().describe('Seconds to travel between stops'),
|
|
2094
|
+
tilt: z.number().optional().describe('Camera tilt applied at each stop'),
|
|
2095
|
+
loop: z.boolean().optional().describe('Loop the tour continuously'),
|
|
2096
|
+
easing: z.string().optional().describe('Easing curve for the swing between stops'),
|
|
2097
|
+
highlightColors: z.array(z.string()).optional().describe('Highlight colors cycled per stop'),
|
|
2098
|
+
showLabels: z.boolean().optional().describe('Show a label at each stop'),
|
|
2099
|
+
labelColor: z.string().optional().describe('Label text color'),
|
|
2100
|
+
}).describe('World tour input (requires regions or coords)');
|
|
2101
|
+
export const StopWorldTourInputSchema = z.object({
|
|
2102
|
+
id: z.string().optional().describe('Tour id to stop (omit to stop all tours)'),
|
|
2103
|
+
}).describe('Stop world tour input');
|
|
2104
|
+
export const PinToGlobeInputSchema = z.object({
|
|
2105
|
+
itemId: z.string().describe('ID of the canvas item to pin to the globe surface'),
|
|
2106
|
+
lon: z.number().describe('Longitude to pin the item at'),
|
|
2107
|
+
lat: z.number().describe('Latitude to pin the item at'),
|
|
2108
|
+
hideOnFarSide: z.boolean().optional().describe('Hide the item when its coordinate rotates to the far side'),
|
|
2109
|
+
}).describe('Pin item to globe input');
|
|
2110
|
+
export const TourItemInputSchema = z.object({
|
|
2111
|
+
itemId: z.string().describe('ID of the canvas item to move along the tour'),
|
|
2112
|
+
regions: z.array(z.string()).optional().describe('Region IDs to tour the item across'),
|
|
2113
|
+
coords: z.array(z.tuple([z.number(), z.number()])).optional().describe('Coordinate stops [lon, lat][] to move the item along'),
|
|
2114
|
+
dwell: z.number().optional().describe('Seconds to dwell at each stop'),
|
|
2115
|
+
travel: z.number().optional().describe('Seconds to travel between stops'),
|
|
2116
|
+
loop: z.boolean().optional().describe('Loop the item tour continuously'),
|
|
2117
|
+
orient: z.boolean().optional().describe('Orient the item along its direction of travel'),
|
|
2118
|
+
}).describe('Tour item along coords/regions input (requires regions or coords)');
|
|
2119
|
+
// =============================================================================
|
|
1743
2120
|
// TEMPLATE SCHEMAS
|
|
1744
2121
|
// =============================================================================
|
|
1745
2122
|
export const TemplateCategorySchema = z.enum([
|
|
@@ -1772,6 +2149,23 @@ export const AnalyzeDesignInputSchema = z.object({
|
|
|
1772
2149
|
export const ValidateDesignInputSchema = z.object({
|
|
1773
2150
|
definition: z.object({}).passthrough().describe('A template or scene definition object to validate and score'),
|
|
1774
2151
|
}).describe('Validate design input');
|
|
2152
|
+
// Semantic scene/op validator (OntologyValidator in FxTool) — distinct from
|
|
2153
|
+
// ValidateDesign (which scores a template definition server-side). Runs in the
|
|
2154
|
+
// browser against the live scene and returns structured diagnostics.
|
|
2155
|
+
export const ValidateInputSchema = z.object({
|
|
2156
|
+
mode: z.enum(['scene', 'op']).default('scene')
|
|
2157
|
+
.describe("'scene' audits the whole live canvas; 'op' validates a proposed mutation before applying it"),
|
|
2158
|
+
op: z.object({
|
|
2159
|
+
kind: z.enum(['addRelation', 'create', 'modify']).describe('The kind of mutation being validated'),
|
|
2160
|
+
from: z.string().optional().describe('Source item id (addRelation)'),
|
|
2161
|
+
to: z.string().optional().describe('Target item id (addRelation)'),
|
|
2162
|
+
relation: z.string().optional().describe('Relation type (addRelation)'),
|
|
2163
|
+
params: z.record(z.unknown()).optional().describe('Relation params (addRelation)'),
|
|
2164
|
+
type: z.string().optional().describe('Item type (create)'),
|
|
2165
|
+
id: z.string().optional().describe('Item id (modify)'),
|
|
2166
|
+
changes: z.record(z.unknown()).optional().describe('Property changes (modify)'),
|
|
2167
|
+
}).optional().describe("Required when mode='op': the proposed mutation to validate"),
|
|
2168
|
+
}).describe('Semantic validation input');
|
|
1775
2169
|
export const QueryOntologyInputSchema = z.object({
|
|
1776
2170
|
query: z.enum([
|
|
1777
2171
|
'list_types', 'list_edges', 'list_generators', 'list_effects',
|
|
@@ -2016,4 +2410,200 @@ export const MeasurementInputSchema = z.object({
|
|
|
2016
2410
|
enabled: z.boolean().optional(),
|
|
2017
2411
|
itemId: z.string().optional(),
|
|
2018
2412
|
});
|
|
2413
|
+
// Geometry construction helpers (Layer 1) — pure functions exposed by FxTool's
|
|
2414
|
+
// app.geometry (GeometryConstruction). The server computes a construction and
|
|
2415
|
+
// optionally creates an item from the result, mirroring:
|
|
2416
|
+
// app.create('polygon', { points: app.geometry.regularPolygon(cx, cy, r, 5) });
|
|
2417
|
+
export const GEOMETRY_OPERATIONS = [
|
|
2418
|
+
// points
|
|
2419
|
+
'point', 'distance', 'midpoint', 'lerpPoint', 'centroid',
|
|
2420
|
+
'translatePoint', 'rotatePoint', 'scalePoint',
|
|
2421
|
+
// polygons
|
|
2422
|
+
'regularPolygon', 'star', 'polygonFromVertices',
|
|
2423
|
+
// lines
|
|
2424
|
+
'lineThrough', 'lineDirection', 'lineIntersection', 'projectPointToLine',
|
|
2425
|
+
'perpendicular', 'parallel', 'perpendicularBisector', 'reflectPoint',
|
|
2426
|
+
// angles
|
|
2427
|
+
'angle', 'angleBisector',
|
|
2428
|
+
// circles
|
|
2429
|
+
'circle', 'pointOnCircle', 'circumcenter', 'circleThrough', 'incenter',
|
|
2430
|
+
'tangentPointsFromExternal',
|
|
2431
|
+
];
|
|
2432
|
+
export const GeometryInputSchema = z.object({
|
|
2433
|
+
operation: z.enum(GEOMETRY_OPERATIONS).describe('Which app.geometry.* construction helper to run'),
|
|
2434
|
+
args: z.array(z.any()).default([]).describe('Positional arguments for the operation, in order. Points are {x,y} objects (or [x,y] arrays where a vertex list is accepted); lines are {p1,p2}; circles are {center,radius}. Angles are in radians. e.g. regularPolygon → [cx, cy, radius, sides, rotation?]'),
|
|
2435
|
+
createAs: z
|
|
2436
|
+
.object({
|
|
2437
|
+
itemType: z.string().optional().describe('Override item type for point results (default "circle").'),
|
|
2438
|
+
color: z.string().optional(),
|
|
2439
|
+
fillColor: z.string().optional(),
|
|
2440
|
+
strokeColor: z.string().optional(),
|
|
2441
|
+
strokeWidth: z.number().optional(),
|
|
2442
|
+
radius: z.number().optional().describe('Marker radius for point results (default 6).'),
|
|
2443
|
+
})
|
|
2444
|
+
.passthrough()
|
|
2445
|
+
.optional()
|
|
2446
|
+
.describe('When provided, also create a canvas item from the result: a vertex-list result → polygon, a circle result → circle, a point result → a small marker. Extra keys pass through to app.create. Omit for compute-only.'),
|
|
2447
|
+
});
|
|
2448
|
+
// Construction sequences (Layer 3) — step-by-step reveal of a geometric figure,
|
|
2449
|
+
// wrapping FxTool's app.constructionSequence. Each step hides its items and attaches
|
|
2450
|
+
// a timeline-driven construction_reveal relation, so playback reveals the figure one
|
|
2451
|
+
// step at a time (replayable, scrubbable, editable as relation graph data).
|
|
2452
|
+
export const ConstructionSequenceInputSchema = z.object({
|
|
2453
|
+
action: z.enum(['build', 'play', 'clear', 'list']).describe('build a sequence, play it on the timeline, clear (restore items), or list sequences'),
|
|
2454
|
+
steps: z
|
|
2455
|
+
.array(z.union([
|
|
2456
|
+
z.array(z.string()),
|
|
2457
|
+
z.object({ items: z.array(z.string()), label: z.string().optional() }),
|
|
2458
|
+
]))
|
|
2459
|
+
.optional()
|
|
2460
|
+
.describe('For build: ordered steps; each step is an array of item ids (or {items, label}) revealed together. Step i reveals at i * stepDuration seconds.'),
|
|
2461
|
+
stepDuration: z.number().positive().optional().describe('For build: seconds between steps (default 1).'),
|
|
2462
|
+
fadeIn: z.number().min(0).optional().describe('For build: per-step fade-in duration in seconds (default 0.3).'),
|
|
2463
|
+
sequenceId: z.string().optional().describe('For play/clear: the cseq_ id returned by build. Defaults to the most recently built sequence.'),
|
|
2464
|
+
loop: z.boolean().optional().describe('For play: loop the timeline (default false).'),
|
|
2465
|
+
duration: z.number().positive().optional().describe('For play: override total play duration in seconds (default = sequence total).'),
|
|
2466
|
+
});
|
|
2467
|
+
// Live-scene semantic validation (OntologyValidator / S2) — structured diagnostics
|
|
2468
|
+
// over the actual canvas (dangling refs, unknown types/props, keyframe issues, cycles).
|
|
2469
|
+
export const ValidateSceneInputSchema = z.object({
|
|
2470
|
+
ops: z
|
|
2471
|
+
.array(z.record(z.string(), z.any()))
|
|
2472
|
+
.optional()
|
|
2473
|
+
.describe('Optional: validate a batch of PROPOSED ops ({ kind: "addRelation"|"create"|"modify"|"delete", ... }) against the live scene before applying them, instead of auditing the current scene. Ids created earlier in the batch count as existing for later ops.'),
|
|
2474
|
+
});
|
|
2475
|
+
// Deterministic headless frame capture (S3) — evaluate the scene at each time via
|
|
2476
|
+
// sceneAt(t) with Math.random seeded once around the whole sequence, then snapshot.
|
|
2477
|
+
export const CaptureFramesInputSchema = z.object({
|
|
2478
|
+
times: z.array(z.number()).min(1).describe('Capture times in seconds, rendered in order.'),
|
|
2479
|
+
seed: z.number().optional().describe('Seed Math.random once around the whole sequence so random generators/particles are reproducible (default 0).'),
|
|
2480
|
+
includeDataUrls: z.boolean().optional().describe('Include each frame as a PNG data URL (large — token-heavy). Default false → returns a cheap per-frame hash + byte size only.'),
|
|
2481
|
+
});
|
|
2482
|
+
// Ontology→scene compiler (S12-E3). Compiles a pp:-namespaced JSON-LD design graph
|
|
2483
|
+
// (typed nodes + structural edges like pp:onTopOf) into a real scene. Compilation
|
|
2484
|
+
// runs server-side (deterministic, testable); only the resulting create/addRelation
|
|
2485
|
+
// ops are emitted. Only ROOT nodes get coordinates — the rest are placed at runtime
|
|
2486
|
+
// by their structural relations.
|
|
2487
|
+
export const InstantiateOntologyInputSchema = z.object({
|
|
2488
|
+
doc: z.record(z.string(), z.unknown()).describe('The design graph: pp:nodes / pp:edges (DesignGraph.toJsonLd shape), or { nodes, edges }, or nodes carrying inline pp: relation properties.'),
|
|
2489
|
+
canvas: z.object({
|
|
2490
|
+
width: z.number().positive(),
|
|
2491
|
+
height: z.number().positive(),
|
|
2492
|
+
}).optional().describe('Canvas size used to tile component roots (default 1080×1080).'),
|
|
2493
|
+
defaultGeometry: z.record(z.string(), z.object({
|
|
2494
|
+
width: z.number(),
|
|
2495
|
+
height: z.number(),
|
|
2496
|
+
})).optional().describe('Per-type default { width, height } overrides (e.g. { text: { width: 300, height: 60 } }).'),
|
|
2497
|
+
});
|
|
2498
|
+
// Relational-density lint of the LIVE scene (S12-E2). Reports how much of the scene
|
|
2499
|
+
// participates in the relation graph and suggests structural relations for unrelated
|
|
2500
|
+
// items already sitting in a structural configuration. Advisory (nothing applied).
|
|
2501
|
+
export const LintSceneInputSchema = z.object({
|
|
2502
|
+
eps: z.number().positive().optional().describe('Bounds-match tolerance in px for structural-configuration detection (default 4).'),
|
|
2503
|
+
cap: z.number().int().positive().optional().describe('Max number of suggestions to return, ranked by confidence (default 20).'),
|
|
2504
|
+
});
|
|
2505
|
+
// Media (video/audio) via window.PinePaperAgent. Agent-facing surface is URL-based
|
|
2506
|
+
// (the agent can't hand over a File). Uploaded media are first-class canvas items.
|
|
2507
|
+
export const MediaInputSchema = z.object({
|
|
2508
|
+
action: z.enum(['upload_video', 'upload_audio', 'list', 'remove', 'set_playback_rate', 'set_clip'])
|
|
2509
|
+
.describe("'upload_video' / 'upload_audio' (from a URL) · 'list' media · 'remove' by id · 'set_playback_rate' · 'set_clip' (re-trim an existing clip)"),
|
|
2510
|
+
url: z.string().url().optional().describe('Media URL — required for upload_video / upload_audio (fetched then imported).'),
|
|
2511
|
+
id: z.string().optional().describe('Media id — required for remove / set_playback_rate / set_clip.'),
|
|
2512
|
+
rate: z.number().min(0.25).max(4).optional().describe('Playback rate 0.25–4 — required for set_playback_rate.'),
|
|
2513
|
+
inPoint: z.number().min(0).optional().describe('Clip in-point in media-time seconds — required for set_clip.'),
|
|
2514
|
+
outPoint: z.number().min(0).optional().describe('Clip out-point in media-time seconds — required for set_clip.'),
|
|
2515
|
+
// upload_video placement
|
|
2516
|
+
position: PositionSchema.optional().describe('Canvas position — upload_video.'),
|
|
2517
|
+
scale: z.number().positive().optional().describe('Scale factor — upload_video.'),
|
|
2518
|
+
timeOffset: z.number().optional().describe('Start offset on the timeline in seconds — upload_video / upload_audio.'),
|
|
2519
|
+
clipInPoint: z.number().optional().describe('Trim in-point in seconds — upload_video.'),
|
|
2520
|
+
clipOutPoint: z.number().optional().describe('Trim out-point in seconds — upload_video.'),
|
|
2521
|
+
// upload_audio
|
|
2522
|
+
volume: z.number().min(0).max(1).optional().describe('Volume 0–1 (default 1) — upload_audio.'),
|
|
2523
|
+
loop: z.boolean().optional().describe('Loop playback (default true) — upload_audio.'),
|
|
2524
|
+
muted: z.boolean().optional().describe('Start muted (default false) — upload_audio.'),
|
|
2525
|
+
})
|
|
2526
|
+
.refine((v) => !(v.action === 'upload_video' || v.action === 'upload_audio') || !!v.url, { message: 'upload requires url', path: ['url'] })
|
|
2527
|
+
.refine((v) => !(v.action === 'remove' || v.action === 'set_playback_rate' || v.action === 'set_clip') || !!v.id, { message: 'this action requires id', path: ['id'] })
|
|
2528
|
+
.refine((v) => v.action !== 'set_playback_rate' || v.rate !== undefined, { message: 'set_playback_rate requires rate', path: ['rate'] })
|
|
2529
|
+
.refine((v) => v.action !== 'set_clip' || (v.inPoint !== undefined && v.outPoint !== undefined && v.outPoint > v.inPoint), { message: 'set_clip requires inPoint and outPoint with outPoint > inPoint', path: ['outPoint'] });
|
|
2530
|
+
// =============================================================================
|
|
2531
|
+
// ONE-SHOT IMAGE OPS (crop / chroma key — Track A agent parity, 2026-07-31)
|
|
2532
|
+
// =============================================================================
|
|
2533
|
+
export const CropImageInputSchema = z.object({
|
|
2534
|
+
itemId: z.string().describe('Registry id of the raster item (or a group containing one).'),
|
|
2535
|
+
rect: z.object({
|
|
2536
|
+
x: z.number().describe('Left edge, canvas coordinates'),
|
|
2537
|
+
y: z.number().describe('Top edge, canvas coordinates'),
|
|
2538
|
+
width: z.number().positive().describe('Crop width in canvas units'),
|
|
2539
|
+
height: z.number().positive().describe('Crop height in canvas units'),
|
|
2540
|
+
}).describe('Crop rectangle in project (canvas) coordinates — clamped into the image bounds.'),
|
|
2541
|
+
aspectRatio: z.enum(['free', '1:1', '16:9', '9:16', '4:3', '3:4', '3:2', '2:3']).optional()
|
|
2542
|
+
.describe('Optional aspect-ratio constraint applied to the rect.'),
|
|
2543
|
+
});
|
|
2544
|
+
export const ChromaKeyInputSchema = z.object({
|
|
2545
|
+
itemId: z.string().describe('Registry id of the raster item (or a group containing one).'),
|
|
2546
|
+
color: z.string().optional()
|
|
2547
|
+
.describe("Key color: '#rrggbb' or preset name (green, chromaGreen, blue, chromaBlue, magenta). Default: pure green."),
|
|
2548
|
+
threshold: z.number().min(0).max(255).optional()
|
|
2549
|
+
.describe('Color-distance threshold for full transparency. Omit to auto-estimate from the image.'),
|
|
2550
|
+
smoothing: z.number().min(0).max(100).optional()
|
|
2551
|
+
.describe('Edge-smoothing range past the threshold. Omit to auto-estimate.'),
|
|
2552
|
+
});
|
|
2553
|
+
// Rigging (skeletons, bones, IK, breakdown-pose keyframes) via app.riggingSystem.
|
|
2554
|
+
// One consolidated tool: build a skeleton, then pose/animate it. The S12 breakdown-pose
|
|
2555
|
+
// options (favor/breakdown/curve/boneOffsets/movingHold/holdDrift) ride add_pose_keyframe.
|
|
2556
|
+
export const RiggingInputSchema = z.object({
|
|
2557
|
+
action: z.enum([
|
|
2558
|
+
'create_skeleton', 'add_bone', 'attach_item', 'create_ik_chain',
|
|
2559
|
+
'add_pose_keyframe', 'set_target_path', 'save_pose', 'save_shape_key',
|
|
2560
|
+
]).describe('Rigging operation'),
|
|
2561
|
+
// identifiers
|
|
2562
|
+
skeletonId: z.string().optional().describe('Skeleton id — required for every action except create_skeleton.'),
|
|
2563
|
+
boneId: z.string().optional().describe('Bone id — attach_item.'),
|
|
2564
|
+
itemId: z.string().optional().describe('Canvas item id — attach_item.'),
|
|
2565
|
+
chainId: z.string().optional().describe('IK chain id — set_target_path.'),
|
|
2566
|
+
name: z.string().optional().describe('Name — create_skeleton / add_bone / create_ik_chain / save_pose / save_shape_key.'),
|
|
2567
|
+
// create_skeleton
|
|
2568
|
+
rootPosition: PositionSchema.optional().describe('Skeleton root position (default {x:400,y:300}) — create_skeleton.'),
|
|
2569
|
+
// add_bone
|
|
2570
|
+
parentBoneId: z.string().optional().describe('Parent bone id (omit for root) — add_bone.'),
|
|
2571
|
+
length: z.number().positive().optional().describe('Bone length px (default 80) — add_bone.'),
|
|
2572
|
+
angle: z.number().optional().describe('Bone rest angle in degrees — add_bone.'),
|
|
2573
|
+
flexibility: z.number().min(0).max(1).optional().describe('0 rigid … 1 fully flexible — add_bone.'),
|
|
2574
|
+
segments: z.number().int().min(0).optional().describe('Curve segments (0 = auto) — add_bone.'),
|
|
2575
|
+
// attach_item
|
|
2576
|
+
attachPoint: z.number().min(0).max(1).optional().describe('Position along bone 0=joint…1=tip (default 0.5) — attach_item.'),
|
|
2577
|
+
// create_ik_chain
|
|
2578
|
+
boneIds: z.array(z.string()).min(2).optional().describe('Ordered bone ids (≥2) — create_ik_chain.'),
|
|
2579
|
+
solverType: z.enum(['fabrik', 'two_bone', 'ccd']).optional().describe('IK solver (default fabrik) — create_ik_chain.'),
|
|
2580
|
+
iterations: z.number().int().positive().optional().describe('Solver iterations (default 10) — create_ik_chain.'),
|
|
2581
|
+
tolerance: z.number().positive().optional().describe('Solver tolerance px (default 0.5) — create_ik_chain.'),
|
|
2582
|
+
strength: z.number().optional().describe('Chain strength 0..1 (default 1) — create_ik_chain.'),
|
|
2583
|
+
poleVector: PositionSchema.optional().describe('Pole vector hint — create_ik_chain.'),
|
|
2584
|
+
// add_pose_keyframe
|
|
2585
|
+
time: z.number().optional().describe('Keyframe time in seconds — add_pose_keyframe.'),
|
|
2586
|
+
pose: z.union([z.string(), z.record(z.string(), z.number())]).optional().describe('A saved pose id, or a { boneId: angleDeg } map — add_pose_keyframe.'),
|
|
2587
|
+
easing: z.string().optional().describe('Named easing (default linear) — add_pose_keyframe.'),
|
|
2588
|
+
favor: z.number().min(-1).max(1).optional().describe('Breakdown spacing bias −1..1 (− favors prev key, + favors next) — add_pose_keyframe.'),
|
|
2589
|
+
breakdown: z.boolean().optional().describe('Mark as a breakdown pose (vs a storytelling key) — add_pose_keyframe.'),
|
|
2590
|
+
curve: z.array(z.number()).optional().describe('Per-segment cubic-bezier ease [x1,y1,x2,y2] (overrides named easing) — add_pose_keyframe.'),
|
|
2591
|
+
boneOffsets: z.record(z.string(), z.number()).optional().describe('Per-bone lag { boneId: 0..0.95 } for overlap/follow-through — add_pose_keyframe.'),
|
|
2592
|
+
movingHold: z.boolean().optional().describe('Hold drifts toward the next key instead of dead-stopping — add_pose_keyframe.'),
|
|
2593
|
+
holdDrift: z.number().optional().describe('Fraction toward the next key the moving hold drifts (default 0.06) — add_pose_keyframe.'),
|
|
2594
|
+
// set_target_path
|
|
2595
|
+
waypoints: z.array(z.object({
|
|
2596
|
+
x: z.number(), y: z.number(),
|
|
2597
|
+
out: PositionSchema.optional(),
|
|
2598
|
+
in: PositionSchema.optional(),
|
|
2599
|
+
})).optional().describe('IK effector path waypoints with optional bezier tangents — set_target_path.'),
|
|
2600
|
+
duration: z.number().positive().optional().describe('Seconds to traverse the path (default 1) — set_target_path.'),
|
|
2601
|
+
loop: z.boolean().optional().describe('Cycle the path — set_target_path.'),
|
|
2602
|
+
})
|
|
2603
|
+
.refine((v) => v.action === 'create_skeleton' || !!v.skeletonId, { message: 'this action requires skeletonId', path: ['skeletonId'] })
|
|
2604
|
+
.refine((v) => v.action !== 'add_bone' || v.skeletonId != null, { message: 'add_bone requires skeletonId', path: ['skeletonId'] })
|
|
2605
|
+
.refine((v) => v.action !== 'attach_item' || (!!v.boneId && !!v.itemId), { message: 'attach_item requires boneId and itemId', path: ['boneId'] })
|
|
2606
|
+
.refine((v) => v.action !== 'create_ik_chain' || (!!v.boneIds && v.boneIds.length >= 2), { message: 'create_ik_chain requires boneIds (≥2)', path: ['boneIds'] })
|
|
2607
|
+
.refine((v) => v.action !== 'add_pose_keyframe' || (v.time !== undefined && v.pose !== undefined), { message: 'add_pose_keyframe requires time and pose', path: ['pose'] })
|
|
2608
|
+
.refine((v) => v.action !== 'set_target_path' || (!!v.chainId && !!v.waypoints), { message: 'set_target_path requires chainId and waypoints', path: ['waypoints'] });
|
|
2019
2609
|
//# sourceMappingURL=schemas.js.map
|