@pinepaper.studio/mcp-server 1.6.18 → 1.6.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +1 -1
- package/README.es.md +1 -1
- package/README.fr.md +1 -1
- package/README.hi.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +179 -10
- package/README.pt-BR.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/agent/export-manager.js +2 -2
- package/dist/agent/session-manager.d.ts +8 -0
- package/dist/agent/session-manager.d.ts.map +1 -1
- package/dist/agent/session-manager.js +17 -2
- package/dist/agent/session-manager.js.map +1 -1
- package/dist/assets/asset-manager.d.ts +28 -1
- package/dist/assets/asset-manager.d.ts.map +1 -1
- package/dist/assets/asset-manager.js +65 -25
- package/dist/assets/asset-manager.js.map +1 -1
- package/dist/assets/index.d.ts +1 -3
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +1 -3
- package/dist/assets/index.js.map +1 -1
- package/dist/assets/repositories/iconify.js +1 -1
- package/dist/assets/repositories/iconify.js.map +1 -1
- package/dist/assets/repositories/openclipart.js +1 -1
- package/dist/assets/repositories/openclipart.js.map +1 -1
- package/dist/assets/repositories/svgrepo.js +2 -2
- package/dist/assets/repositories/svgrepo.js.map +1 -1
- package/dist/assets/types.d.ts +2 -2
- package/dist/assets/types.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.d.ts +25 -0
- package/dist/browser/puppeteer-controller.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.js +107 -1
- package/dist/browser/puppeteer-controller.js.map +1 -1
- package/dist/build-info.json +3 -3
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/design/design-systems.d.ts +2 -0
- package/dist/design/design-systems.d.ts.map +1 -1
- package/dist/design/design-systems.js +2 -0
- package/dist/design/design-systems.js.map +1 -1
- package/dist/execution/canvas-state.d.ts.map +1 -1
- package/dist/execution/canvas-state.js +19 -43
- package/dist/execution/canvas-state.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -11
- package/dist/index.js.map +1 -1
- package/dist/ontology/design-graph.js +2 -2
- package/dist/ontology/design-graph.js.map +1 -1
- package/dist/ontology/upstream-vocabulary.d.ts +2 -2
- package/dist/ontology/upstream-vocabulary.js +2 -2
- package/dist/ontology/validator.js +1 -1
- package/dist/ontology/validator.js.map +1 -1
- package/dist/ontology/vocabulary.d.ts.map +1 -1
- package/dist/ontology/vocabulary.js +8 -0
- package/dist/ontology/vocabulary.js.map +1 -1
- package/dist/tools/choreograph.d.ts +64 -0
- package/dist/tools/choreograph.d.ts.map +1 -0
- package/dist/tools/choreograph.js +336 -0
- package/dist/tools/choreograph.js.map +1 -0
- package/dist/tools/compact-descriptions.d.ts.map +1 -1
- package/dist/tools/compact-descriptions.js +16 -15
- package/dist/tools/compact-descriptions.js.map +1 -1
- package/dist/tools/definitions.d.ts +10 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +705 -150
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/effect-check.d.ts +26 -0
- package/dist/tools/effect-check.d.ts.map +1 -0
- package/dist/tools/effect-check.js +129 -0
- package/dist/tools/effect-check.js.map +1 -0
- package/dist/tools/effect-class.d.ts +31 -0
- package/dist/tools/effect-class.d.ts.map +1 -0
- package/dist/tools/effect-class.js +170 -0
- package/dist/tools/effect-class.js.map +1 -0
- package/dist/tools/engine-lists.d.ts +20 -5
- package/dist/tools/engine-lists.d.ts.map +1 -1
- package/dist/tools/engine-lists.js +106 -4
- package/dist/tools/engine-lists.js.map +1 -1
- package/dist/tools/engine-methods.d.ts.map +1 -1
- package/dist/tools/engine-methods.js +22 -1
- package/dist/tools/engine-methods.js.map +1 -1
- package/dist/tools/engine-surface.d.ts +1 -1
- package/dist/tools/engine-surface.d.ts.map +1 -1
- package/dist/tools/engine-surface.js +232 -13
- package/dist/tools/engine-surface.js.map +1 -1
- package/dist/tools/handlers/camera.js +8 -8
- package/dist/tools/handlers/camera.js.map +1 -1
- package/dist/tools/handlers/export.d.ts.map +1 -1
- package/dist/tools/handlers/export.js +84 -5
- package/dist/tools/handlers/export.js.map +1 -1
- package/dist/tools/handlers/font.js +10 -10
- package/dist/tools/handlers/font.js.map +1 -1
- package/dist/tools/handlers/generate.d.ts +12 -12
- package/dist/tools/handlers/maps.d.ts.map +1 -1
- package/dist/tools/handlers/maps.js +23 -3
- package/dist/tools/handlers/maps.js.map +1 -1
- package/dist/tools/handlers.d.ts +44 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +413 -39
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +39 -26
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/shape-params.d.ts +2 -2
- package/dist/tools/shape-params.d.ts.map +1 -1
- package/dist/tools/shape-params.js +27 -2
- package/dist/tools/shape-params.js.map +1 -1
- package/dist/tools/stick-vocabulary.d.ts +1 -1
- package/dist/tools/stick-vocabulary.js +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +11 -0
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +91 -3
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +1544 -276
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +3203 -907
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +393 -53
- package/dist/types/schemas.js.map +1 -1
- package/manifest.json +46 -2
- package/package.json +4 -3
package/dist/types/schemas.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 3. TypeScript type inference
|
|
9
9
|
*/
|
|
10
10
|
import { z } from 'zod';
|
|
11
|
-
import { ENGINE_EASING_NAMES, DESIGN_MEDIA, DESIGN_MEDIA_APPLY } from '../tools/engine-lists.js';
|
|
11
|
+
import { ENGINE_EASING_NAMES, DESIGN_MEDIA, DESIGN_MEDIA_APPLY, FILTER_TYPES, LINEAGE_KINDS, STYLED_SCENE_STYLES } from '../tools/engine-lists.js';
|
|
12
12
|
// =============================================================================
|
|
13
13
|
// COMMON SCHEMAS
|
|
14
14
|
// =============================================================================
|
|
@@ -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',
|
|
@@ -316,6 +326,8 @@ export const KeyframeSchema = z.object({
|
|
|
316
326
|
time: z.number().describe('Time in seconds for this keyframe'),
|
|
317
327
|
properties: z.record(z.unknown()).describe('Property values at this keyframe'),
|
|
318
328
|
easing: KeyframeEasingSchema.optional().default('linear'),
|
|
329
|
+
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."),
|
|
330
|
+
hold: z.boolean().optional().describe("Same as interpolation: 'hold'."),
|
|
319
331
|
});
|
|
320
332
|
// =============================================================================
|
|
321
333
|
// RELATION TYPES
|
|
@@ -396,6 +408,10 @@ export const RelationTypeSchema = z.enum([
|
|
|
396
408
|
'on_event_toggle',
|
|
397
409
|
'on_event_store_set',
|
|
398
410
|
'on_event_store_increment',
|
|
411
|
+
'on_event_play_sound',
|
|
412
|
+
'looks_at',
|
|
413
|
+
'reacts_to',
|
|
414
|
+
'drag_to_turn',
|
|
399
415
|
// --- Proximity — the pointer entering or leaving an item ---
|
|
400
416
|
'on_enter_set_color',
|
|
401
417
|
'on_enter_set_property',
|
|
@@ -724,6 +740,7 @@ export const EventInputSchema = z.object({
|
|
|
724
740
|
payloadType: z.string().optional().describe("Payload type label (default 'Pulse') — action 'create'"),
|
|
725
741
|
x: z.number().optional().describe('Canvas x for the event marker — action create'),
|
|
726
742
|
y: z.number().optional().describe('Canvas y for the event marker — action create'),
|
|
743
|
+
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
744
|
eventId: z.string().optional().describe("Event id to fire — required for action 'pulse'"),
|
|
728
745
|
payload: z.unknown().optional().describe("Optional value forwarded to listeners — action 'pulse'"),
|
|
729
746
|
}).refine((v) => v.action !== 'create' || !!v.name, { message: "action 'create' requires name", path: ['name'] })
|
|
@@ -1105,22 +1122,10 @@ export const BlastParamsSchema = z.object({
|
|
|
1105
1122
|
// 'saturate' is what the published JSON schema offered for releases, while
|
|
1106
1123
|
// the engine registers 'saturation' and this enum rejected 'saturate' — a
|
|
1107
1124
|
// caller who followed the docs got a validation error. Accepted and mapped.
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
'brightness',
|
|
1113
|
-
'contrast',
|
|
1114
|
-
'saturation',
|
|
1115
|
-
'invert',
|
|
1116
|
-
'noise',
|
|
1117
|
-
'vignette',
|
|
1118
|
-
'vintage',
|
|
1119
|
-
'colorOverlay',
|
|
1120
|
-
'sharpen',
|
|
1121
|
-
'emboss',
|
|
1122
|
-
'posterize',
|
|
1123
|
-
])).describe('Type of visual filter to apply');
|
|
1125
|
+
// GENERATED list (FilterSystem.js registerFilter, scripts/sync-engine-lists.mjs).
|
|
1126
|
+
// The hand copy here hid six of twenty: dither, halftoneCMYK, halftoneDots,
|
|
1127
|
+
// edgeDetect, hsl and colorTint. 'saturate' stays accepted as 'saturation'.
|
|
1128
|
+
export const FilterTypeSchema = z.preprocess((v) => (v === 'saturate' ? 'saturation' : v), z.enum(FILTER_TYPES)).describe('Type of visual filter to apply');
|
|
1124
1129
|
// =============================================================================
|
|
1125
1130
|
// TOOL INPUT SCHEMAS
|
|
1126
1131
|
// =============================================================================
|
|
@@ -1162,6 +1167,40 @@ export const ItemDataFlagsSchema = z.object({
|
|
|
1162
1167
|
isDraggable: z.boolean().optional(),
|
|
1163
1168
|
isDecorative: z.boolean().optional(),
|
|
1164
1169
|
});
|
|
1170
|
+
/**
|
|
1171
|
+
* Per-item engine setters applied after create / modify (FxTool D20, D11).
|
|
1172
|
+
* Sent through their own engine calls, never as create params: an engine
|
|
1173
|
+
* without them would ignore an unknown param in silence, and the call says so
|
|
1174
|
+
* instead.
|
|
1175
|
+
*/
|
|
1176
|
+
export const ScreenSpaceSchema = z.boolean()
|
|
1177
|
+
.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.');
|
|
1178
|
+
export const StepTimingSchema = z.union([
|
|
1179
|
+
z.number().int().min(1),
|
|
1180
|
+
z.object({ every: z.number().int().min(1), baseFps: z.number().positive().optional() }).strict(),
|
|
1181
|
+
z.object({ fps: z.number().positive() }).strict(),
|
|
1182
|
+
z.null(),
|
|
1183
|
+
]).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.");
|
|
1184
|
+
/** A text item's counter (FxTool D70): a number that counts on the scene clock. */
|
|
1185
|
+
export const CounterSchema = z.union([
|
|
1186
|
+
z.object({
|
|
1187
|
+
from: z.number(), to: z.number(),
|
|
1188
|
+
start: z.number().min(0).optional().describe('Seconds into the timeline (default 0).'),
|
|
1189
|
+
duration: z.number().positive().optional().describe('Seconds to count (default 1).'),
|
|
1190
|
+
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."),
|
|
1191
|
+
decimals: z.number().int().min(0).max(10).optional().describe('Default: the precision from / to were written with.'),
|
|
1192
|
+
prefix: z.string().optional(), suffix: z.string().optional(),
|
|
1193
|
+
locale: z.string().optional().describe("Number format locale (default 'en-US')."),
|
|
1194
|
+
grouping: z.boolean().optional().describe('Thousands separators (default true).'),
|
|
1195
|
+
padStart: z.number().int().min(1).max(21).optional().describe('Zero-pad to this many digits.'),
|
|
1196
|
+
steps: z.number().int().positive().optional().describe('Count in N jumps instead of smoothly.'),
|
|
1197
|
+
tabular: z.boolean().optional().describe('Fixed-width digits so the width stays steady (default true).'),
|
|
1198
|
+
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."),
|
|
1199
|
+
eraNegative: z.string().optional().describe("Era label for negative years (default 'BCE')."),
|
|
1200
|
+
eraPositive: z.string().optional().describe("Era label for positive years (default 'CE'; '' for a bare year; 'AD' works)."),
|
|
1201
|
+
}).strict(),
|
|
1202
|
+
z.null(),
|
|
1203
|
+
]).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.");
|
|
1165
1204
|
export const CreateItemInputSchema = z.object({
|
|
1166
1205
|
itemType: ItemTypeSchema,
|
|
1167
1206
|
position: PositionSchema.optional().default({ x: 400, y: 300 }),
|
|
@@ -1176,6 +1215,9 @@ export const CreateItemInputSchema = z.object({
|
|
|
1176
1215
|
// is read beside position as often as inside properties — and at the top
|
|
1177
1216
|
// level it was stripped without a word (round 7 retest, 1.63).
|
|
1178
1217
|
anchor: z.string().optional().describe("Which point position names: 'center' (default), 'top-left', 'top-right', 'bottom-left', 'bottom-right'. Same as properties.anchor."),
|
|
1218
|
+
screenSpace: ScreenSpaceSchema.optional(),
|
|
1219
|
+
stepTiming: StepTimingSchema.optional(),
|
|
1220
|
+
counter: CounterSchema.optional(),
|
|
1179
1221
|
});
|
|
1180
1222
|
// Light direction for 3D effects
|
|
1181
1223
|
export const LightDirectionSchema = z.enum([
|
|
@@ -1215,6 +1257,9 @@ export const ModifyItemInputSchema = z.object({
|
|
|
1215
1257
|
data: ItemDataFlagsSchema.optional(),
|
|
1216
1258
|
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.'),
|
|
1217
1259
|
easing: z.string().optional().describe('atTime: easing of the segment arriving at the new key (default linear).'),
|
|
1260
|
+
screenSpace: ScreenSpaceSchema.optional(),
|
|
1261
|
+
stepTiming: StepTimingSchema.optional(),
|
|
1262
|
+
counter: CounterSchema.optional(),
|
|
1218
1263
|
});
|
|
1219
1264
|
// Delete Item
|
|
1220
1265
|
export const DeleteItemInputSchema = z.object({
|
|
@@ -1441,9 +1486,12 @@ export const SetCanvasSizeInputSchema = z.object({
|
|
|
1441
1486
|
// not manage it.
|
|
1442
1487
|
// The refusal NAMES the route: a 728x90 banner refused with a bare "too
|
|
1443
1488
|
// small" left a caller to export the preset instead (round 8 BB, 1.48).
|
|
1444
|
-
width: z.number().min(100, "a canvas side under 100 px is not supported. For small assets (a 728x90 or 320x50 banner, a 28 px emote) design at 2-4x — e.g. 1456x180 — and export with agent_export region {x:0, y:0, width, height, outputWidth:728, outputHeight:90}. For VIDEO at a small size, design at 2x and export with agent_export scale: 0.5.").max(16384).describe('Canvas width (100-16384; above 8192 Safari / iOS may fail)'),
|
|
1445
|
-
height: z.number().min(100, "a canvas side under 100 px is not supported. For small assets (a 728x90 or 320x50 banner, a 28 px emote) design at 2-4x — e.g. 1456x180 — and export with agent_export region {x:0, y:0, width, height, outputWidth:728, outputHeight:90}. For VIDEO at a small size, design at 2x and export with agent_export scale: 0.5.").max(16384).describe('Canvas height (100-16384; above 8192 Safari / iOS may fail)'),
|
|
1446
|
-
preset: z.string().optional().describe('
|
|
1489
|
+
width: z.number().min(100, "a canvas side under 100 px is not supported. For small assets (a 728x90 or 320x50 banner, a 28 px emote) design at 2-4x — e.g. 1456x180 — and export with agent_export region {x:0, y:0, width, height, outputWidth:728, outputHeight:90}. For VIDEO at a small size, design at 2x and export with agent_export scale: 0.5.").max(16384).optional().describe('Canvas width (100-16384; above 8192 Safari / iOS may fail)'),
|
|
1490
|
+
height: z.number().min(100, "a canvas side under 100 px is not supported. For small assets (a 728x90 or 320x50 banner, a 28 px emote) design at 2-4x — e.g. 1456x180 — and export with agent_export region {x:0, y:0, width, height, outputWidth:728, outputHeight:90}. For VIDEO at a small size, design at 2x and export with agent_export scale: 0.5.").max(16384).optional().describe('Canvas height (100-16384; above 8192 Safari / iOS may fail)'),
|
|
1491
|
+
preset: z.string().optional().describe('A named size instead of width/height; an unknown name is refused with the known ones listed.'),
|
|
1492
|
+
}).refine((v) => !!v.preset || (v.width !== undefined && v.height !== undefined), {
|
|
1493
|
+
// {preset} alone was refused for a missing width (1.6.19 gate).
|
|
1494
|
+
message: 'pass width and height, or a preset', path: ['width'],
|
|
1447
1495
|
});
|
|
1448
1496
|
// Export
|
|
1449
1497
|
export const ExportSVGInputSchema = z.object({
|
|
@@ -1521,12 +1569,12 @@ export const SetToolkitInputSchema = z.object({
|
|
|
1521
1569
|
// =============================================================================
|
|
1522
1570
|
export const SearchAssetsInputSchema = z.object({
|
|
1523
1571
|
query: z.string().describe('Search term (e.g., "rocket", "heart", "user icon")'),
|
|
1524
|
-
repository: z.enum(['all', '
|
|
1572
|
+
repository: z.enum(['all', 'iconify', 'fontawesome']).optional().describe('Which repository to search (default: all)'),
|
|
1525
1573
|
limit: z.number().min(1).max(50).optional().describe('Max results to return (default: 10)'),
|
|
1526
1574
|
includeSvgContent: z.boolean().optional().describe('Include actual SVG content for each result (default: false). When true, fetches and returns the SVG string for each asset, enabling AI to display or describe the icons.'),
|
|
1527
1575
|
});
|
|
1528
1576
|
export const ImportAssetInputSchema = z.object({
|
|
1529
|
-
assetId: z.string().optional().describe('Asset ID from search results (e.g., "
|
|
1577
|
+
assetId: z.string().optional().describe('Asset ID from search results (e.g., "iconify_mdi_rocket")'),
|
|
1530
1578
|
url: z.string().optional().describe('Direct URL to SVG (alternative to assetId)'),
|
|
1531
1579
|
position: z.object({
|
|
1532
1580
|
x: z.number(),
|
|
@@ -1738,8 +1786,23 @@ export const CreateDiagramShapeInputSchema = z.object({
|
|
|
1738
1786
|
/**
|
|
1739
1787
|
* Connect items input
|
|
1740
1788
|
*/
|
|
1741
|
-
|
|
1742
|
-
|
|
1789
|
+
// strokeColor / strokeWidth are what a model guesses (Paper's names); they
|
|
1790
|
+
// were stripped and the connector drew in the default colour (gate run 3).
|
|
1791
|
+
export const ConnectInputSchema = z.preprocess((v) => {
|
|
1792
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
1793
|
+
return v;
|
|
1794
|
+
const o = { ...v };
|
|
1795
|
+
if (o.lineColor === undefined && o.strokeColor !== undefined) {
|
|
1796
|
+
o.lineColor = o.strokeColor;
|
|
1797
|
+
delete o.strokeColor;
|
|
1798
|
+
}
|
|
1799
|
+
if (o.lineWidth === undefined && o.strokeWidth !== undefined) {
|
|
1800
|
+
o.lineWidth = o.strokeWidth;
|
|
1801
|
+
delete o.strokeWidth;
|
|
1802
|
+
}
|
|
1803
|
+
return o;
|
|
1804
|
+
}, z.object({
|
|
1805
|
+
id: z.string().optional().describe('Requested connector id. The studio may not honour it (the result says idHonoured): always use the connectorId the result returns for update/remove.'),
|
|
1743
1806
|
sourceItemId: z.string().describe('Registry ID of source item'),
|
|
1744
1807
|
targetItemId: z.string().describe('Registry ID of target item'),
|
|
1745
1808
|
routing: ConnectorRoutingSchema.optional().default('orthogonal'),
|
|
@@ -1752,12 +1815,12 @@ export const ConnectInputSchema = z.object({
|
|
|
1752
1815
|
curvature: z.number().min(0.1).max(1.0).optional().default(0.5).describe('Curve intensity for curved routing'),
|
|
1753
1816
|
boltEnabled: z.boolean().optional().default(true).describe('Enable animated bolt effect'),
|
|
1754
1817
|
boltColor: z.string().optional().default('#fbbf24').describe('Bolt animation color'),
|
|
1755
|
-
});
|
|
1818
|
+
}));
|
|
1756
1819
|
/**
|
|
1757
1820
|
* Connect specific ports input
|
|
1758
1821
|
*/
|
|
1759
1822
|
export const ConnectPortsInputSchema = z.object({
|
|
1760
|
-
id: z.string().optional().describe('
|
|
1823
|
+
id: z.string().optional().describe('Requested connector id. The studio may not honour it (the result says idHonoured): always use the connectorId the result returns for update/remove.'),
|
|
1761
1824
|
sourceItemId: z.string().describe('Registry ID of source item'),
|
|
1762
1825
|
sourcePort: PortPositionSchema.describe('Port position on source'),
|
|
1763
1826
|
targetItemId: z.string().describe('Registry ID of target item'),
|
|
@@ -1824,16 +1887,34 @@ export const GetDiagramShapesInputSchema = z.object({
|
|
|
1824
1887
|
/**
|
|
1825
1888
|
* Update connector input
|
|
1826
1889
|
*/
|
|
1890
|
+
// Paper.js's own names are what a model guesses for a connector's style, and
|
|
1891
|
+
// an unknown key was STRIPPED, so {strokeColor} changed nothing and reported
|
|
1892
|
+
// success (gate run 3). The Paper names are mapped; anything else is refused
|
|
1893
|
+
// with the keys that work.
|
|
1894
|
+
const CONNECTOR_STYLE_ALIASES = { strokeColor: 'lineColor', color: 'lineColor', strokeWidth: 'lineWidth', width: 'lineWidth' };
|
|
1895
|
+
const CONNECTOR_STYLE_KEYS = ['lineColor', 'lineWidth', 'headStyle', 'tailStyle', 'routing', 'lineStyle'];
|
|
1827
1896
|
export const UpdateConnectorInputSchema = z.object({
|
|
1828
1897
|
connectorId: z.string().describe('Connector ID to update'),
|
|
1829
|
-
style: z.
|
|
1898
|
+
style: z.preprocess((v) => {
|
|
1899
|
+
if (!v || typeof v !== 'object' || Array.isArray(v))
|
|
1900
|
+
return v;
|
|
1901
|
+
const out = {};
|
|
1902
|
+
for (const [k, val] of Object.entries(v)) {
|
|
1903
|
+
if (k === 'dashArray') {
|
|
1904
|
+
out.lineStyle = Array.isArray(val) && val.length ? 'dashed' : 'solid';
|
|
1905
|
+
continue;
|
|
1906
|
+
}
|
|
1907
|
+
out[CONNECTOR_STYLE_ALIASES[k] ?? k] = val;
|
|
1908
|
+
}
|
|
1909
|
+
return out;
|
|
1910
|
+
}, z.object({
|
|
1830
1911
|
lineColor: z.string().optional(),
|
|
1831
1912
|
lineWidth: z.number().optional(),
|
|
1832
1913
|
headStyle: ArrowHeadStyleSchema.optional(),
|
|
1833
1914
|
tailStyle: ArrowHeadStyleSchema.optional(),
|
|
1834
1915
|
routing: ConnectorRoutingSchema.optional(),
|
|
1835
1916
|
lineStyle: ConnectorLineStyleSchema.optional(),
|
|
1836
|
-
}).optional(),
|
|
1917
|
+
}).strict(`a connector's style takes ${CONNECTOR_STYLE_KEYS.join(', ')} (strokeColor, strokeWidth and dashArray are accepted for lineColor, lineWidth and lineStyle)`)).optional(),
|
|
1837
1918
|
label: z.string().optional().describe('Update connector label'),
|
|
1838
1919
|
labelPosition: z.number().min(0).max(1).optional().describe('Label position along path (0-1)'),
|
|
1839
1920
|
});
|
|
@@ -2039,11 +2120,12 @@ export const AgentBatchOperationSchema = z.object({
|
|
|
2039
2120
|
type: AgentBatchOperationTypeSchema,
|
|
2040
2121
|
// Create operation fields
|
|
2041
2122
|
itemType: z.string().optional().describe('Item type for create operations'),
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
y: z.number(),
|
|
2045
|
-
}).optional().describe('Position for create operations'),
|
|
2123
|
+
// PositionSchema, as create_item takes: [x, y] was refused here (1.6.19 gate).
|
|
2124
|
+
position: PositionSchema.optional().describe('Position for create operations: {x, y} or [x, y]'),
|
|
2046
2125
|
properties: z.record(z.unknown()).optional().describe('Properties for create/modify operations'),
|
|
2126
|
+
screenSpace: ScreenSpaceSchema.optional().describe('create: keep the item fixed in the frame under camera moves (HUD).'),
|
|
2127
|
+
stepTiming: StepTimingSchema.optional().describe("create: step this item's animation (N, {every, baseFps?} or {fps})."),
|
|
2128
|
+
counter: CounterSchema.optional().describe('create (text): count on the scene clock.'),
|
|
2047
2129
|
// Modify/Animate/Delete/Keyframe/Mask/Effect target
|
|
2048
2130
|
itemId: z.string().optional().describe('Target item ID or $N reference'),
|
|
2049
2131
|
// Group operation fields
|
|
@@ -2171,6 +2253,19 @@ export const GIF_MAX_DURATION_S = 15;
|
|
|
2171
2253
|
* out-of-memory — it turns into a message naming the ceiling and what to do.
|
|
2172
2254
|
*/
|
|
2173
2255
|
export const VIDEO_MAX_DURATION_S = 600;
|
|
2256
|
+
/**
|
|
2257
|
+
* pinepaper_styled_scene (FxTool K2): one declarative composition drawn in a
|
|
2258
|
+
* built-in style, as ONE canvas item that redraws itself every exported frame.
|
|
2259
|
+
* The styles are GENERATED (STYLED_SCENE_STYLES); the engine also resolves its
|
|
2260
|
+
* own aliases ('watercolour', 'papercut'), so style is a string here and an
|
|
2261
|
+
* unknown one is refused by the engine with the known list.
|
|
2262
|
+
*/
|
|
2263
|
+
export const StyledSceneInputSchema = z.object({
|
|
2264
|
+
style: z.string().optional().describe(`The look: ${STYLED_SCENE_STYLES.join(' | ')} (default watercolor). The engine also takes its aliases (watercolour, papercut, 1bit…).`),
|
|
2265
|
+
duration: z.number().positive().max(VIDEO_MAX_DURATION_S).optional().describe('Seconds the scene runs (default the composition\'s, 8). Export for this long.'),
|
|
2266
|
+
id: z.string().optional().describe('Scene id (default "styled-scene"). Calling again with the same id REPLACES that scene, e.g. to change style.'),
|
|
2267
|
+
spec: z.record(z.string(), z.unknown()).optional().describe("The composition, every key optional (omitted keys take the 'valley' preset): preset, size [W, H], duration, transition {name, start, length}, camera {zoom: [from, to], pan}, palette, sun, clouds, ridges, foreground, river, settlements, structure, pylons, vegetation, effects {steam, ripples, scars}, hud {label}."),
|
|
2268
|
+
});
|
|
2174
2269
|
/**
|
|
2175
2270
|
* Smart export input schema
|
|
2176
2271
|
*/
|
|
@@ -2591,6 +2686,7 @@ export const HighlightRegionsInputSchema = z.object({
|
|
|
2591
2686
|
strokeColor: z.string().optional().describe('Highlight stroke color'),
|
|
2592
2687
|
stroke: z.string().optional().describe("The engine's own name for the stroke."),
|
|
2593
2688
|
strokeWidth: z.number().optional().describe('Highlight stroke width'),
|
|
2689
|
+
opacity: z.number().min(0).max(1).optional().describe('0..1: a TINT over the land. The colour is pre-blended over the region\'s land fill and drawn opaque, so the export shows exactly the fill reported. Hex colours only.'),
|
|
2594
2690
|
animate: z.boolean().optional().describe('Animate the highlight'),
|
|
2595
2691
|
}).optional().describe('Highlight options'),
|
|
2596
2692
|
}).describe('Highlight regions input');
|
|
@@ -2656,12 +2752,24 @@ export const GetRegionAtPointInputSchema = z.object({
|
|
|
2656
2752
|
y: z.number().describe('Canvas Y coordinate'),
|
|
2657
2753
|
}).describe('Get region at point input');
|
|
2658
2754
|
// Map Animation Schemas
|
|
2659
|
-
|
|
2755
|
+
// The engine reads fillColor OR color, plus easing (MapSystem.animateRegions);
|
|
2756
|
+
// requiring fillColor refused keyframes written with color (gate D17, 1.6.19).
|
|
2757
|
+
// A keyframe may nest its values under `properties`, the shape every other
|
|
2758
|
+
// keyframe tool takes; it is flattened rather than refused (gate run 2).
|
|
2759
|
+
export const MapRegionKeyframeSchema = z.preprocess((k) => {
|
|
2760
|
+
if (!k || typeof k !== 'object' || Array.isArray(k))
|
|
2761
|
+
return k;
|
|
2762
|
+
const { properties, ...rest } = k;
|
|
2763
|
+
return properties && typeof properties === 'object' && !Array.isArray(properties) ? { ...properties, ...rest } : k;
|
|
2764
|
+
}, z.object({
|
|
2660
2765
|
time: z.number().describe('Time in seconds'),
|
|
2661
|
-
fillColor: z.string().describe('Fill color at this keyframe'),
|
|
2766
|
+
fillColor: z.string().optional().describe('Fill color at this keyframe (or color)'),
|
|
2767
|
+
color: z.string().optional().describe('Same as fillColor'),
|
|
2662
2768
|
strokeColor: z.string().optional().describe('Stroke color (optional)'),
|
|
2663
2769
|
opacity: z.number().optional().describe('Opacity 0-1 (optional)'),
|
|
2664
|
-
|
|
2770
|
+
easing: z.string().optional().describe('Easing into this keyframe'),
|
|
2771
|
+
}).refine((k) => !!(k.fillColor || k.color), { message: 'each keyframe needs fillColor (or color)', path: ['fillColor'] }))
|
|
2772
|
+
.describe('Map region keyframe');
|
|
2665
2773
|
export const WaveDirectionSchema = z.enum(['horizontal', 'vertical', 'radial']).describe('Wave animation direction');
|
|
2666
2774
|
export const AnimateMapRegionsInputSchema = z.object({
|
|
2667
2775
|
duration: z.number().optional().default(5).describe('Total animation duration in seconds'),
|
|
@@ -2671,9 +2779,10 @@ export const AnimateMapRegionsInputSchema = z.object({
|
|
|
2671
2779
|
export const AnimateMapWaveInputSchema = z.object({
|
|
2672
2780
|
duration: z.number().optional().default(10).describe('Total wave duration in seconds'),
|
|
2673
2781
|
loop: z.boolean().optional().default(true).describe('Loop the animation'),
|
|
2674
|
-
colors: z.array(z.string()).optional().describe('
|
|
2782
|
+
colors: z.array(z.string()).optional().describe('Colors the wave cycles through (at least 2)'),
|
|
2675
2783
|
waveDirection: WaveDirectionSchema.optional().default('horizontal').describe('Direction of wave effect'),
|
|
2676
|
-
|
|
2784
|
+
waveSpeed: z.number().positive().optional().describe('Speed of the wave (default 1)'),
|
|
2785
|
+
}).describe('Animate map wave input: the wave runs across ALL regions of the map');
|
|
2677
2786
|
export const StopMapAnimationsInputSchema = z.object({
|
|
2678
2787
|
regions: z.array(z.string()).optional().describe('Specific region IDs to stop (omit for all)'),
|
|
2679
2788
|
resetColors: z.boolean().optional().default(true).describe('Reset regions to default colors'),
|
|
@@ -2865,6 +2974,8 @@ export const ScenePlaybackInputSchema = z.object({
|
|
|
2865
2974
|
defaultDuration: z.number().optional(),
|
|
2866
2975
|
defaultTransition: z.enum(['none', 'fade', 'zoom-in', 'zoom-out']).optional(),
|
|
2867
2976
|
transitionDuration: z.number().optional(),
|
|
2977
|
+
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()
|
|
2978
|
+
.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})."),
|
|
2868
2979
|
enabled: z.boolean().optional(),
|
|
2869
2980
|
index: z.number().optional(),
|
|
2870
2981
|
});
|
|
@@ -3069,7 +3180,7 @@ export const TransformInputSchema = z.object({
|
|
|
3069
3180
|
// `pinepaper_media` / `pinepaper_transform` set the precedent.
|
|
3070
3181
|
export const BrandKitInputSchema = z.object({
|
|
3071
3182
|
/** 'plan' reports what WOULD change without touching the scene. 'from_url' reads a site into a proposed kit. */
|
|
3072
|
-
action: z.enum(['plan', 'apply', 'from_url']),
|
|
3183
|
+
action: z.enum(['plan', 'apply', 'from_url', 'set', 'get', 'clear']),
|
|
3073
3184
|
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."),
|
|
3074
3185
|
screenshots: z.number().int().min(1).max(6).optional().describe('from_url: screenshots taken down the page (default 3).'),
|
|
3075
3186
|
// Spelled out rather than a loose record. A first version typed this as
|
|
@@ -3095,7 +3206,7 @@ export const BrandKitInputSchema = z.object({
|
|
|
3095
3206
|
}).optional(),
|
|
3096
3207
|
selectionOnly: z.boolean().optional(),
|
|
3097
3208
|
})
|
|
3098
|
-
.refine((v) =>
|
|
3209
|
+
.refine((v) => !['plan', 'apply', 'set'].includes(v.action) || !!v.kit, { message: 'plan, apply and set require kit (from_url proposes one)', path: ['kit'] })
|
|
3099
3210
|
.refine((v) => v.action !== 'from_url' || (!!v.url && /^https?:\/\//i.test(v.url)), { message: 'from_url requires an http(s) url', path: ['url'] });
|
|
3100
3211
|
export const ComponentInputSchema = z.object({
|
|
3101
3212
|
action: z.enum(['define', 'list', 'instantiate', 'set_override', 'sync', 'update_from_instance', 'detach']),
|
|
@@ -3135,7 +3246,9 @@ export const CommentInputSchema = z.object({
|
|
|
3135
3246
|
export const ProvenanceInputSchema = z.object({
|
|
3136
3247
|
action: z.enum(['get', 'lineage', 'dependents', 'record']),
|
|
3137
3248
|
itemId: z.string(),
|
|
3138
|
-
|
|
3249
|
+
// GENERATED (LINEAGE_KINDS): the default was 'derived', which the engine
|
|
3250
|
+
// refuses, and the docs named three kinds that do not exist (1.6.19 gate).
|
|
3251
|
+
kind: z.enum(LINEAGE_KINDS).optional().describe('record: how the item came from sourceRef (default derivedFrom).'),
|
|
3139
3252
|
sourceRef: z.string().optional(),
|
|
3140
3253
|
meta: z.record(z.string(), z.unknown()).optional(),
|
|
3141
3254
|
});
|
|
@@ -3539,6 +3652,41 @@ export const ValidateSceneInputSchema = z.object({
|
|
|
3539
3652
|
* deterministic capture; the times come from `times`, or from one of the
|
|
3540
3653
|
* modes below.
|
|
3541
3654
|
*/
|
|
3655
|
+
/**
|
|
3656
|
+
* pinepaper_export_scene / pinepaper_import_scene (D28): a restorable save.
|
|
3657
|
+
* The default export is a summary for inspection; `full` writes the engine's
|
|
3658
|
+
* project document (captureProjectDocument) to a file.
|
|
3659
|
+
*/
|
|
3660
|
+
/**
|
|
3661
|
+
* pinepaper_place_on_surface (D22, FxTool #56): a UI onto a screen in a photo,
|
|
3662
|
+
* corner-pinned, matted and lit, baked into the photo's pixels.
|
|
3663
|
+
*/
|
|
3664
|
+
const PointPair = z.tuple([z.number(), z.number()]);
|
|
3665
|
+
export const PlaceOnSurfaceInputSchema = z.object({
|
|
3666
|
+
photoId: z.string().describe('The photo: an image item (or a group holding one).'),
|
|
3667
|
+
sourceId: z.string().describe('The UI to place: any canvas item or group, or an image item.'),
|
|
3668
|
+
quad: z.tuple([PointPair, PointPair, PointPair, PointPair]).optional().describe('The screen\'s four corners TL, TR, BR, BL. Omit to detect it (the largest bright, neutral region).'),
|
|
3669
|
+
quadSpace: z.enum(['image', 'canvas']).optional().describe("quad's coordinates: 'image' (default) photo pixels, or 'canvas'."),
|
|
3670
|
+
occlusion: z.enum(['luma', 'none']).optional().describe("'luma' (default) keeps a thumb or hand in front of the screen; 'none' covers it."),
|
|
3671
|
+
key: z.number().optional().describe('Brightness above which the photo counts as screen (default: found from the photo).'),
|
|
3672
|
+
erode: z.number().min(0).optional().describe('Pull the matte in from the bezel, px (default 2).'),
|
|
3673
|
+
matteBlur: z.number().min(0).optional().describe('Soften the matte edge (default 0.8).'),
|
|
3674
|
+
soften: z.number().min(0).optional().describe('Soften the UI to the photo\'s focus (default 0.5).'),
|
|
3675
|
+
glare: z.number().min(0).max(1).optional().describe('Glass glare kept over the UI (default 0.10).'),
|
|
3676
|
+
spill: z.number().min(0).max(1).optional().describe('Screen light spilling onto the surroundings (default 0.35).'),
|
|
3677
|
+
spillRadius: z.number().min(0).optional().describe('Spill reach, px (default 40).'),
|
|
3678
|
+
tint: z.tuple([z.number(), z.number(), z.number()]).optional().describe('Multiply the UI by [r, g, b], 0..1, to match the photo\'s white balance.'),
|
|
3679
|
+
slot: z.string().optional().describe("Name for this screen (default 'screen'). Placing again in the same slot REPLACES it; several screens in one photo use different slots."),
|
|
3680
|
+
hideSource: z.boolean().optional().describe('Hide the UI item after placing it (default true).'),
|
|
3681
|
+
});
|
|
3682
|
+
export const ExportSceneInputSchema = z.object({
|
|
3683
|
+
full: z.boolean().optional().describe('Save a RESTORABLE project document to a file (keyframes, relations, rigging, scene chains), not the summary. Restore it with pinepaper_import_scene.'),
|
|
3684
|
+
name: z.string().optional().describe('full: a name recorded in the document.'),
|
|
3685
|
+
});
|
|
3686
|
+
export const ImportSceneInputSchema = z.object({
|
|
3687
|
+
path: z.string().min(1).describe('The project document a full pinepaper_export_scene saved (absolute path, or relative to the server working directory).'),
|
|
3688
|
+
strict: z.boolean().optional().describe('Refuse a document with validation problems (default true). false loads anyway and returns the problems as warnings.'),
|
|
3689
|
+
});
|
|
3542
3690
|
export const CaptureSheetSchema = z.object({
|
|
3543
3691
|
every: z.number().positive().optional().describe('One frame every N seconds from 0 to duration (needs duration).'),
|
|
3544
3692
|
duration: z.number().positive().optional().describe('Scene length in seconds, for every and loopSeam.'),
|
|
@@ -3571,7 +3719,7 @@ export const InstantiateOntologyInputSchema = z.object({
|
|
|
3571
3719
|
canvas: z.object({
|
|
3572
3720
|
width: z.number().positive(),
|
|
3573
3721
|
height: z.number().positive(),
|
|
3574
|
-
}).optional().describe('Canvas size
|
|
3722
|
+
}).optional().describe('Canvas size to lay out for (default: the live canvas).'),
|
|
3575
3723
|
defaultGeometry: z.record(z.string(), z.object({
|
|
3576
3724
|
width: z.number(),
|
|
3577
3725
|
height: z.number(),
|
|
@@ -3684,7 +3832,26 @@ export const TextStyleInputSchema = z.object({
|
|
|
3684
3832
|
scale: z.number().optional().describe('Size multiplier (default 1).'),
|
|
3685
3833
|
strokeColor: z.string().optional().describe("Ink colour (default '#ffffff')."),
|
|
3686
3834
|
strokeWidth: z.number().optional().describe('Pen width (default 2).'),
|
|
3687
|
-
|
|
3835
|
+
nib: z.union([
|
|
3836
|
+
z.boolean(),
|
|
3837
|
+
z.enum(['monoline', 'broad']),
|
|
3838
|
+
z.object({
|
|
3839
|
+
angle: z.number().optional().describe('Nib angle in degrees.'),
|
|
3840
|
+
width: z.number().positive().optional().describe('Widest stroke, in px.'),
|
|
3841
|
+
contrast: z.number().min(0).max(1).optional().describe('0..1; the hairline is width × (1 − contrast).'),
|
|
3842
|
+
taper: z.number().min(0).max(0.5).optional().describe('0..0.5; thin entry and exit.'),
|
|
3843
|
+
}).strict(),
|
|
3844
|
+
]).optional().describe("The pen. 'monoline' (or false): one even line. 'broad' (or true): a broad nib, thick across it and hairline along it; an object tunes the broad nib. Omitted: the studio's default pen."),
|
|
3845
|
+
// Draw-on (gate D41): the engine keyframes trimEnd 0→1 itself; these were
|
|
3846
|
+
// stripped, so a handwritten title arrived fully drawn.
|
|
3847
|
+
animate: z.boolean().optional().describe('true writes the words on, stroke by stroke, over `duration` seconds — no keyframe_animate needed (default false: drawn at once).'),
|
|
3848
|
+
duration: z.number().positive().optional().describe('animate: seconds to write the whole line (default 2).'),
|
|
3849
|
+
easing: z.enum(ENGINE_EASING_NAMES).optional().describe("animate: the write-on easing (default 'easeOut')."),
|
|
3850
|
+
timeOffset: z.number().min(0).optional().describe('animate: seconds into the timeline before writing starts (default 0).'),
|
|
3851
|
+
singleStroke: z.boolean().optional().describe('Primary strokes only — drop the secondary strokes (default false).'),
|
|
3852
|
+
showGhost: z.boolean().optional().describe('Show a faint outline of the whole line under the write-on (default false).'),
|
|
3853
|
+
tension: z.number().optional().describe("Curve tension of the pen path (the studio's default when omitted)."),
|
|
3854
|
+
}).strict().optional().describe('cursive: where and how it is written. The result is a STROKED PATH, so pinepaper_animate\'s draw-on and pinepaper_path\'s outline_stroke both apply to it — which is the reason to prefer it over a script font.'),
|
|
3688
3855
|
collageOptions: z.record(z.string(), z.unknown()).optional().describe('to_collage: style, palette and the rest, same vocabulary as pinepaper_create_letter_collage.'),
|
|
3689
3856
|
itemId: z.string().optional().describe('Text item id — apply_style / set_font_axes.'),
|
|
3690
3857
|
styleKey: z.string().optional().describe("apply_style: style name from list_styles (e.g. 'stacked', 'arcade' — the pixel/arcade styles suggest their own face)."),
|
|
@@ -3708,7 +3875,13 @@ export const TextStyleInputSchema = z.object({
|
|
|
3708
3875
|
}).passthrough().optional().describe('set_font_axes: weight/width/slant, or OpenType tags (wght/wdth/slnt). Unknown axes are passed to the engine and come back in `rejected` rather than being dropped here. All three named axes are animatable (addKeyframe with fontWeight interpolates).'),
|
|
3709
3876
|
})
|
|
3710
3877
|
.refine((v) => v.action !== 'apply_style' || (!!v.itemId && !!v.styleKey), { message: 'apply_style requires itemId and styleKey', path: ['styleKey'] })
|
|
3711
|
-
.refine((v) => v.action !== 'set_font_axes' || (!!v.itemId && !!v.axes && Object.keys(v.axes).length > 0), { message: 'set_font_axes requires itemId and at least one axis', path: ['axes'] })
|
|
3878
|
+
.refine((v) => v.action !== 'set_font_axes' || (!!v.itemId && !!v.axes && Object.keys(v.axes).length > 0), { message: 'set_font_axes requires itemId and at least one axis', path: ['axes'] })
|
|
3879
|
+
// cursive WRITES new words; with no text it reached the engine and failed as
|
|
3880
|
+
// "Cannot read properties of undefined (reading 'length')" (gate, P2).
|
|
3881
|
+
.refine((v) => v.action !== 'cursive' || (typeof v.text === 'string' && v.text.trim() !== ''), {
|
|
3882
|
+
message: "cursive writes new text from `text`; it does not restyle an existing item. Pass text (the words to write); for a style on an existing text item use apply_style with its itemId.",
|
|
3883
|
+
path: ['text']
|
|
3884
|
+
});
|
|
3712
3885
|
/**
|
|
3713
3886
|
* pinepaper_text_effect — the 37 character-level text effects
|
|
3714
3887
|
* (terminaltexteffects' vocabulary, reimplemented in FxTool from source).
|
|
@@ -3737,9 +3910,9 @@ export const TextStyleInputSchema = z.object({
|
|
|
3737
3910
|
* `list_media` before promising a medium in prose.
|
|
3738
3911
|
*/
|
|
3739
3912
|
export const DesignMediumInputSchema = z.object({
|
|
3740
|
-
action: z.enum(['list_media', 'resolve', 'list_stitches', 'apply_thread', 'apply_hatch', 'list_flow_fields', 'list_hatch_options', 'apply'])
|
|
3913
|
+
action: z.enum(['list_media', 'resolve', 'list_stitches', 'apply_thread', 'apply_hatch', 'list_flow_fields', 'list_hatch_options', 'apply', 'sdf_stroke'])
|
|
3741
3914
|
.describe("'apply' (render an item in ANY medium the engine can make: medium + options — watercolor, oil, cutPaper, thread, hatch) · 'list_media' (every medium with fidelity + limitation) · 'resolve' (can this medium be made here, and how honestly) · 'list_stitches' · 'apply_thread' (render an item in thread) · 'apply_hatch' (rule an item with hatching — value through line density) · 'list_flow_fields' · 'list_hatch_options'"),
|
|
3742
|
-
medium: z.string().optional().describe(`resolve / apply: medium key — ${DESIGN_MEDIA.join(', ')}. apply works for ${Object.keys(DESIGN_MEDIA_APPLY).join(', ')}; the
|
|
3915
|
+
medium: z.string().optional().describe(`resolve / apply: medium key — ${DESIGN_MEDIA.join(', ')}. apply works for ${Object.keys(DESIGN_MEDIA_APPLY).join(', ')}; a medium the studio cannot apply is refused by the engine with the reason. ink and charcoal, where the studio applies them, restyle the item IN PLACE (same id).`),
|
|
3743
3916
|
options: z.record(z.string(), z.unknown()).optional().describe('apply: the medium\'s own options. watercolor: color, brush, water 0..1, opacity, edge 0..1 (the dark rim), angle, bleed, seed, keepSource. oil: color, brush, length, density, toneRange, relief, angle, seed, keepSource. cutPaper: color, facet, jitter, shadow, shadowColor, shadowOpacity, shadowOffset [dx, dy], seed, keepSource. thread / hatch: as apply_thread / apply_hatch.'),
|
|
3744
3917
|
itemId: z.string().optional().describe('apply / apply_thread / apply_hatch: a closed path, compound path (holes kept), or a group of them (applied per path). The result takes the source\'s place in the stacking order and the source is hidden; applying again replaces it. One undo step.'),
|
|
3745
3918
|
stitch: z.enum(THREAD_STITCHES).optional()
|
|
@@ -3756,9 +3929,9 @@ export const DesignMediumInputSchema = z.object({
|
|
|
3756
3929
|
stitchLen: z.number().positive().optional().describe('apply_thread: nominal stitch length px (default 18).'),
|
|
3757
3930
|
rowGap: z.number().positive().optional().describe('apply_thread: row spacing px. Defaults from the thread width so the rows abut; setting it wider is what makes a fill read as hatching.'),
|
|
3758
3931
|
variance: z.number().min(0).max(1).optional().describe('apply_thread: length jitter 0..1 (default 0.35) — the variance IS the long-and-short shading.'),
|
|
3759
|
-
width: z.number().positive().optional().describe('apply_thread: thread width px (default 2).'),
|
|
3932
|
+
width: z.number().positive().optional().describe('apply_thread: thread width px (default 2). sdf_stroke: stroke width px (default 8).'),
|
|
3760
3933
|
sheen: z.boolean().optional().describe('apply_thread: draw the highlight along each stitch (default true) — thread is not matte.'),
|
|
3761
|
-
color: z.string().optional().describe("apply_thread: thread colour, defaulting to the item's own fill."),
|
|
3934
|
+
color: z.string().optional().describe("apply_thread: thread colour, defaulting to the item's own fill. sdf_stroke: stroke colour."),
|
|
3762
3935
|
seed: z.number().int().optional().describe('apply_thread: PRNG seed (default 1). Same seed stitches the same way.'),
|
|
3763
3936
|
count: z.number().int().positive().optional().describe('apply_thread: stitch count for seed/satin.'),
|
|
3764
3937
|
// THE OPTIONS THE ENGINE READS AND NOTHING NAMED. ThreadPainting.paint reads
|
|
@@ -3791,12 +3964,177 @@ export const DesignMediumInputSchema = z.object({
|
|
|
3791
3964
|
flowField: z.enum(['hand', 'curved', 'zigzag', 'waves', 'seabed', 'spiral', 'columns']).optional()
|
|
3792
3965
|
.describe('apply_hatch: bend each line along a flow field. The straight scanline is the ruling; the field is what makes it read as drawn rather than printed. See list_flow_fields.'),
|
|
3793
3966
|
t: z.number().optional().describe('apply_hatch: flow-field time offset — advances the field without changing the seed.'),
|
|
3967
|
+
// --- SDF brush stroke (FxTool D5) ---
|
|
3968
|
+
points: z.array(z.union([
|
|
3969
|
+
z.object({ x: z.number(), y: z.number(), pressure: z.number().min(0).max(1).optional() }),
|
|
3970
|
+
z.tuple([z.number(), z.number()]),
|
|
3971
|
+
z.tuple([z.number(), z.number(), z.number()]),
|
|
3972
|
+
])).min(1).optional().describe('sdf_stroke: the stroke path in canvas px, each {x, y, pressure?} or [x, y, pressure?]. Pressure 0..1 scales the width at that point.'),
|
|
3973
|
+
softness: z.number().min(0).max(1).optional().describe('sdf_stroke: edge softness 0..1 (default 0.2).'),
|
|
3974
|
+
opacity: z.number().min(0).max(1).optional().describe('sdf_stroke: 0..1 (default 1).'),
|
|
3975
|
+
resolution: z.number().positive().optional().describe('sdf_stroke: bitmap pixels per canvas px (default 2).'),
|
|
3976
|
+
name: z.string().optional().describe('sdf_stroke: item name.'),
|
|
3794
3977
|
})
|
|
3795
3978
|
.refine((v) => v.action !== 'apply_thread' || !!v.itemId, { message: 'apply_thread requires itemId', path: ['itemId'] })
|
|
3796
3979
|
.refine((v) => v.action !== 'resolve' || !!v.medium, { message: 'resolve requires medium', path: ['medium'] })
|
|
3980
|
+
.refine((v) => v.action !== 'sdf_stroke' || (Array.isArray(v.points) && v.points.length > 0), { message: 'sdf_stroke requires points', path: ['points'] })
|
|
3797
3981
|
.refine((v) => v.action !== 'apply_hatch' || !!v.itemId, { message: 'apply_hatch requires itemId', path: ['itemId'] })
|
|
3798
3982
|
.refine((v) => v.action !== 'apply' || (!!v.itemId && !!v.medium), { message: 'apply requires itemId and medium', path: ['medium'] })
|
|
3799
3983
|
.refine((v) => v.action !== 'apply' || !v.medium || DESIGN_MEDIA.includes(v.medium), { message: `unknown medium; the engine knows ${DESIGN_MEDIA.join(', ')}`, path: ['medium'] });
|
|
3984
|
+
/**
|
|
3985
|
+
* pinepaper_relight — a lighting pass over the 3D world layer (FxTool D5).
|
|
3986
|
+
* CPU-backed; normals from the world's luminance (no depth). Applied in the
|
|
3987
|
+
* preview and in PNG / PDF / MP4 / WebM; not GIF / APNG; not saved with the
|
|
3988
|
+
* project. Lights are validated by the engine, which names every error.
|
|
3989
|
+
*/
|
|
3990
|
+
const RelightColorSchema = z.union([z.string(), z.tuple([z.number(), z.number(), z.number()])]);
|
|
3991
|
+
export const RelightInputSchema = z.object({
|
|
3992
|
+
action: z.enum(['set', 'get', 'clear', 'list_lights'])
|
|
3993
|
+
.describe("'set' (install or replace the rig) · 'get' (the current rig) · 'clear' (remove it) · 'list_lights' (the light types)"),
|
|
3994
|
+
lights: z.array(z.discriminatedUnion('type', [
|
|
3995
|
+
z.object({
|
|
3996
|
+
type: z.literal('point'),
|
|
3997
|
+
x: z.number(), y: z.number(),
|
|
3998
|
+
z: z.number().optional().describe('Height above the picture, px (default 120).'),
|
|
3999
|
+
radius: z.number().positive().optional().describe('Falloff radius, px (default 400).'),
|
|
4000
|
+
color: RelightColorSchema.optional(),
|
|
4001
|
+
intensity: z.number().min(0).optional().describe('Default 1.'),
|
|
4002
|
+
specular: z.number().min(0).optional().describe('Shininess exponent (default 32).'),
|
|
4003
|
+
}),
|
|
4004
|
+
z.object({
|
|
4005
|
+
type: z.literal('directional'),
|
|
4006
|
+
dir: z.tuple([z.number(), z.number(), z.number()]).describe('[dx, dy, dz]; [0, 0, -1] faces the viewer.'),
|
|
4007
|
+
color: RelightColorSchema.optional(),
|
|
4008
|
+
intensity: z.number().min(0).optional(),
|
|
4009
|
+
}),
|
|
4010
|
+
])).max(8).optional().describe('set: up to 8 lights. Coordinates are export-frame px. Colours are #rrggbb or [r, g, b].'),
|
|
4011
|
+
ambient: z.number().min(0).optional().describe('set: ambient light (default 0.2).'),
|
|
4012
|
+
strength: z.number().positive().optional().describe('set: normal-map strength (default 2).'),
|
|
4013
|
+
enabled: z.boolean().optional().describe('set: false keeps the rig but turns it off.'),
|
|
4014
|
+
preview: z.union([z.boolean(), z.object({ maxSide: z.number().min(64).max(4096) })]).optional()
|
|
4015
|
+
.describe('set: show it in the live preview (capped-resolution overlay); {maxSide} sets the cap.'),
|
|
4016
|
+
}).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'] });
|
|
4017
|
+
/** pinepaper_emitter — the data-driven particle item (FxTool D29). The engine validates the spec and refuses by name. */
|
|
4018
|
+
export const EmitterInputSchema = z.object({
|
|
4019
|
+
action: z.enum(['create', 'set']),
|
|
4020
|
+
itemId: z.string().optional().describe('set: the emitter to change.'),
|
|
4021
|
+
spec: z.record(z.string(), z.unknown()).describe('create: the whole spec. set: the keys to change (merged).'),
|
|
4022
|
+
}).refine((v) => v.action !== 'set' || !!v.itemId, { message: 'set requires itemId', path: ['itemId'] });
|
|
4023
|
+
/** pinepaper_look (D64): restyle an item, a subtree or the whole scene as a named look. */
|
|
4024
|
+
export const LookInputSchema = z.object({
|
|
4025
|
+
action: z.enum(['list', 'describe', 'apply', 'remove', 'get', 'grid']),
|
|
4026
|
+
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.'),
|
|
4027
|
+
target: z.string().optional().describe("apply / remove / get: an itemId, or 'scene' (default)."),
|
|
4028
|
+
scope: z.enum(['item', 'subtree', 'scene']).optional().describe('apply: how far the look reaches (default: the item for an id, the scene for scene).'),
|
|
4029
|
+
looks: z.array(z.string()).optional().describe('grid: which looks (default every one).'),
|
|
4030
|
+
cols: z.number().int().positive().optional(), cellWidth: z.number().positive().optional(), t: z.number().min(0).optional(),
|
|
4031
|
+
})
|
|
4032
|
+
.refine((v) => !['describe', 'apply'].includes(v.action) || v.look !== undefined, { message: 'describe and apply require look', path: ['look'] });
|
|
4033
|
+
/** pinepaper_original_character (D66): an ORIGINAL character from a base rig and a declarative variant. */
|
|
4034
|
+
export const OriginalCharacterInputSchema = z.object({
|
|
4035
|
+
action: z.enum(['bases', 'describe', 'random', 'create']),
|
|
4036
|
+
base: z.string().optional().describe('person | robot | cat | bird | blob (bases lists them).'),
|
|
4037
|
+
variant: z.record(z.string(), z.unknown()).optional().describe('create: {palette, features, look, seed, name} — legal values from describe.'),
|
|
4038
|
+
seed: z.number().int().optional().describe('random: same (base, seed) → the same variant.'),
|
|
4039
|
+
fixed: z.record(z.string(), z.unknown()).optional().describe('random: traits every member shares (a look, proportions), merged over the random choice.'),
|
|
4040
|
+
at: z.object({ x: z.number(), y: z.number() }).optional().describe('create: x, and y = the FEET.'),
|
|
4041
|
+
height: z.number().positive().optional().describe('create: drawn height px (default 320).'),
|
|
4042
|
+
scale: z.number().positive().optional().describe('create: multiplier of the default height.'),
|
|
4043
|
+
rig: z.boolean().optional().describe('create: build the skeleton (default true).'),
|
|
4044
|
+
})
|
|
4045
|
+
.refine((v) => !['random', 'create'].includes(v.action) || !!v.base, { message: 'random and create require base', path: ['base'] })
|
|
4046
|
+
.refine((v) => v.action !== 'random' || v.seed !== undefined, { message: 'random requires seed', path: ['seed'] });
|
|
4047
|
+
/** pinepaper_morph_sequence (D73): one element through N states as a morphs_to chain. */
|
|
4048
|
+
export const MorphSequenceInputSchema = z.object({
|
|
4049
|
+
itemId: z.string().describe('The element that morphs (it keeps its id through every state).'),
|
|
4050
|
+
states: z.array(z.object({
|
|
4051
|
+
at: z.number().min(0).describe('Seconds: when the morph INTO this state starts.'),
|
|
4052
|
+
duration: z.number().positive().optional().describe('Seconds the morph takes (default 0.8).'),
|
|
4053
|
+
to: z.string().optional().describe('An existing item whose shape this state takes (it is hidden; it is only the shape reference).'),
|
|
4054
|
+
shape: z.object({
|
|
4055
|
+
itemType: z.string(),
|
|
4056
|
+
position: z.object({ x: z.number(), y: z.number() }),
|
|
4057
|
+
properties: z.record(z.string(), z.unknown()).optional(),
|
|
4058
|
+
}).optional().describe('Or describe the state: it is created hidden as the reference.'),
|
|
4059
|
+
}).refine((s) => !!s.to !== !!s.shape, { message: 'each state takes exactly one of to or shape' })).min(1).max(40),
|
|
4060
|
+
easing: z.enum(['linear', 'easeIn', 'easeOut', 'easeInOut']).optional().describe('Morph easing (default easeInOut) — the morph supports these four.'),
|
|
4061
|
+
camera: z.union([z.boolean(), z.object({ fill: z.number().min(0.05).max(1).optional(), easing: z.string().optional() })]).optional()
|
|
4062
|
+
.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).'),
|
|
4063
|
+
sound: z.union([z.boolean(), z.string().min(1)]).optional().describe("true: a whoosh at each morph start; or a catalogue sound name."),
|
|
4064
|
+
}).superRefine((v, ctx) => {
|
|
4065
|
+
let end = -Infinity;
|
|
4066
|
+
[...v.states].sort((a, b) => a.at - b.at).forEach((s, i) => {
|
|
4067
|
+
if (s.at < end - 1e-6)
|
|
4068
|
+
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'] });
|
|
4069
|
+
end = s.at + (s.duration ?? 0.8);
|
|
4070
|
+
});
|
|
4071
|
+
});
|
|
4072
|
+
/** pinepaper_render_hook — per-frame drawing saved as SOURCE (FxTool D29). */
|
|
4073
|
+
export const RenderHookInputSchema = z.object({
|
|
4074
|
+
action: z.enum(['register', 'unregister', 'list']),
|
|
4075
|
+
id: z.string().min(1).optional(),
|
|
4076
|
+
source: z.string().min(1).optional().describe('register: JS that evaluates to (ctx, t, info) => {}.'),
|
|
4077
|
+
layer: z.enum(['above', 'below']).optional(),
|
|
4078
|
+
deterministic: z.boolean().optional(),
|
|
4079
|
+
seed: z.number().int().optional(),
|
|
4080
|
+
}).refine((v) => v.action === 'list' || !!v.id, { message: 'register / unregister require id', path: ['id'] })
|
|
4081
|
+
.refine((v) => v.action !== 'register' || !!v.source, { message: 'register requires source', path: ['source'] });
|
|
4082
|
+
/** pinepaper_choreograph — beat verbs compiled to one squash-and-stretch track (D67). */
|
|
4083
|
+
const ChoreoPointSchema = z.union([z.tuple([z.number(), z.number()]), z.object({ x: z.number(), y: z.number() })]);
|
|
4084
|
+
const ChoreoEdgeSchema = z.enum(['left', 'right', 'top', 'bottom']);
|
|
4085
|
+
export const CHOREO_VERBS = ['pop', 'drop', 'hop', 'bounce', 'roll', 'fly', 'peek', 'shake', 'squash', 'enter', 'exit'];
|
|
4086
|
+
export const ChoreographBeatSchema = z.object({
|
|
4087
|
+
at: z.number().min(0).describe('Seconds. Beats run one after another; one that starts before the previous ends is refused.'),
|
|
4088
|
+
verb: z.enum(CHOREO_VERBS),
|
|
4089
|
+
to: ChoreoPointSchema.optional().describe('Where the actor ends up (its centre, canvas px): hop / bounce / roll / fly / drop / enter. roll and fly require it.'),
|
|
4090
|
+
duration: z.number().positive().optional().describe('Seconds for this beat (each verb has a sensible default).'),
|
|
4091
|
+
height: z.number().positive().optional().describe('hop / bounce: jump height px (default 1.2× / 1.5× the actor).'),
|
|
4092
|
+
times: z.number().int().min(1).max(12).optional().describe('bounce: number of bounces (default 3).'),
|
|
4093
|
+
decay: z.number().min(0.1).max(0.95).optional().describe('bounce: each bounce is this fraction of the last (default 0.6).'),
|
|
4094
|
+
arc: z.number().optional().describe('fly: how far above the higher end the path rises, px (default 2× the actor).'),
|
|
4095
|
+
edge: ChoreoEdgeSchema.optional().describe('peek: the frame edge it peeks from (default left). exit: the edge it leaves by (default right).'),
|
|
4096
|
+
from: ChoreoEdgeSchema.optional().describe('enter: the frame edge it comes in from (default left).'),
|
|
4097
|
+
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).'),
|
|
4098
|
+
hold: z.number().min(0).optional().describe('peek: seconds it stays out (default 1).'),
|
|
4099
|
+
intensity: z.number().positive().optional().describe('shake: px each way (default 8% of its width).'),
|
|
4100
|
+
}).strict().superRefine((b, ctx) => {
|
|
4101
|
+
if ((b.verb === 'roll' || b.verb === 'fly') && !b.to)
|
|
4102
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: `${b.verb} needs to: [x, y]`, path: ['to'] });
|
|
4103
|
+
if (b.verb === 'exit' && b.to !== undefined)
|
|
4104
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'exit leaves by an edge: pass edge (left | right | top | bottom), not to', path: ['to'] });
|
|
4105
|
+
});
|
|
4106
|
+
const ChoreoSoundName = z.union([z.string().min(1), z.literal(false)]);
|
|
4107
|
+
export const ChoreographInputSchema = z.object({
|
|
4108
|
+
itemId: z.string().describe('The actor: any item — a shape, a group, an imported SVG character, a photo cut-out.'),
|
|
4109
|
+
beats: z.array(ChoreographBeatSchema).min(1).max(60),
|
|
4110
|
+
sound: z.union([z.boolean(), z.object({
|
|
4111
|
+
pop: ChoreoSoundName.optional(), whoosh: ChoreoSoundName.optional(),
|
|
4112
|
+
contact: ChoreoSoundName.optional(), shake: ChoreoSoundName.optional(),
|
|
4113
|
+
gain: z.number().min(0).max(1).optional(),
|
|
4114
|
+
}).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."),
|
|
4115
|
+
});
|
|
4116
|
+
/**
|
|
4117
|
+
* pinepaper_shader_graph — validated JSON pixel graphs (FxTool D5), CPU-backed.
|
|
4118
|
+
* Linear chains only; the engine names every error (unknown type, duplicate,
|
|
4119
|
+
* dangling edge, cycle, branching).
|
|
4120
|
+
*/
|
|
4121
|
+
const ShaderGraphJsonSchema = z.object({
|
|
4122
|
+
id: z.string().optional(),
|
|
4123
|
+
nodes: z.array(z.object({ id: z.string(), type: z.string(), params: z.record(z.string(), z.unknown()).optional() })).min(1),
|
|
4124
|
+
edges: z.array(z.object({ from: z.string(), to: z.string() })).optional(),
|
|
4125
|
+
});
|
|
4126
|
+
export const ShaderGraphInputSchema = z.object({
|
|
4127
|
+
action: z.enum(['node_types', 'validate', 'create', 'get', 'list', 'remove', 'apply'])
|
|
4128
|
+
.describe("'node_types' (the node vocabulary + params) · 'validate' (check a graph, store nothing) · 'create' (validate + store) · 'get' / 'remove' (by graphId) · 'list' · 'apply' (run a graph over an item or the scene)"),
|
|
4129
|
+
graph: ShaderGraphJsonSchema.optional().describe('validate / create / apply: {id?, nodes: [{id, type, params?}], edges: [{from, to}]} — a linear chain.'),
|
|
4130
|
+
graphId: z.string().optional().describe('get / remove / apply: a stored graph id.'),
|
|
4131
|
+
target: z.string().optional().describe("apply: an itemId, or 'scene'."),
|
|
4132
|
+
keepSource: z.boolean().optional().describe('apply to an item: keep the source visible (default: hidden).'),
|
|
4133
|
+
asItem: z.boolean().optional().describe("apply to 'scene': add the result as an image item instead of returning it."),
|
|
4134
|
+
})
|
|
4135
|
+
.refine((v) => !['validate', 'create'].includes(v.action) || !!v.graph, { message: 'validate / create require graph', path: ['graph'] })
|
|
4136
|
+
.refine((v) => !['get', 'remove'].includes(v.action) || !!v.graphId, { message: 'get / remove require graphId', path: ['graphId'] })
|
|
4137
|
+
.refine((v) => v.action !== 'apply' || (!!v.target && (!!v.graph || !!v.graphId)), { message: "apply requires target (an itemId or 'scene') and graph or graphId", path: ['target'] });
|
|
3800
4138
|
export const TextEffectInputSchema = z.object({
|
|
3801
4139
|
action: z.enum(['apply', 'list'])
|
|
3802
4140
|
.describe("'apply' (explode a text item into animated characters) · 'list' (the 37 effects with key/label/definition, for a picker)"),
|
|
@@ -3985,7 +4323,7 @@ export const World3DInputSchema = z.object({
|
|
|
3985
4323
|
}).optional().describe("add_material / set_material: a NAMED, SHARED surface referenced by many objects, so one edit restyles all of them. metalness and roughness reach the shader on the MESH path only. There are no aoMapIntensity / normalScale / envMapIntensity knobs: each scales a map that does not exist, and a knob that scales nothing is worse than a missing one."),
|
|
3986
4324
|
materialId: z.string().optional().describe('set_material / remove_material: the material id.'),
|
|
3987
4325
|
// --- Import ---------------------------------------------------------------
|
|
3988
|
-
source: z.string().optional().describe('import_obj: the OBJ file TEXT. import_gltf:
|
|
4326
|
+
source: z.string().optional().describe('import_obj: the OBJ file TEXT. import_gltf: the .gltf JSON TEXT with its buffers embedded. A URL, data: URI or file path is NOT fetched (the string is parsed as JSON), so a .glb cannot be imported here.'),
|
|
3989
4327
|
importOptions: z.record(z.string(), z.unknown()).optional().describe('import_obj / import_gltf: placement and scale options, passed through to the engine.'),
|
|
3990
4328
|
clip: z.string().optional().describe('set_mesh_clip: which imported animation clip a skinned mesh plays.'),
|
|
3991
4329
|
crossfade: z.number().optional().describe('set_mesh_clip: seconds to blend out of the clip that was playing. The blend happens in LOCAL pose space — a palette cannot be blended after the fact.'),
|
|
@@ -4098,7 +4436,7 @@ export const DesignSystemInputSchema = z.object({
|
|
|
4098
4436
|
export const StickInputSchema = z.object({
|
|
4099
4437
|
action: z.enum(['figure', 'set']).describe("'figure' a posed, walking or travelling stick figure · 'set' the floor, wall and objects it stands on and among."),
|
|
4100
4438
|
id: z.string().optional().describe('Prefix for the created items.'),
|
|
4101
|
-
kind: z.string().optional().describe("figure: which figure. set: which piece
|
|
4439
|
+
kind: z.string().optional().describe("figure: which figure. set: which piece — room (floor + wall; options groundY, wall, floor), tabletop (object + surfaceY), chair (facing), table, counter, door, shelf (items), window. An unknown kind or tabletop object is refused with the engine's current list."),
|
|
4102
4440
|
at: z.object({ x: z.number().optional(), y: z.number().optional() }).optional().describe('Where it goes.'),
|
|
4103
4441
|
scale: z.number().optional().describe('Size multiplier.'),
|
|
4104
4442
|
facing: z.enum(['left', 'right']).optional().describe("set: which way a CHAIR faces. The figure has no facing — it is ignored on action 'figure', which refuses it by name rather than dropping it."),
|
|
@@ -4145,6 +4483,7 @@ export const StoryInputSchema = z.object({
|
|
|
4145
4483
|
spec: z.record(z.string(), z.unknown()).optional().describe('apply_spec: the story spec — usually one from `distill`, edited.'),
|
|
4146
4484
|
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.'),
|
|
4147
4485
|
options: z.record(z.string(), z.unknown()).optional().describe('Assembly or layout options, passed through.'),
|
|
4486
|
+
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)."),
|
|
4148
4487
|
})
|
|
4149
4488
|
.refine((v) => !['distill', 'from_text'].includes(v.action) || !!v.text, { message: 'this action requires text', path: ['text'] })
|
|
4150
4489
|
.refine((v) => v.action !== 'apply_spec' || !!v.spec, { message: 'apply_spec requires spec', path: ['spec'] })
|
|
@@ -4164,7 +4503,7 @@ export const InterchangeInputSchema = z.object({
|
|
|
4164
4503
|
.describe("'export_lottie' (JSON) · 'export_dotlottie' (the zipped .lottie) · 'import_lottie' (a Lottie or .lottie back onto the canvas) · 'export_glb' (the perspective 3D objects — needs some to exist) · 'export_bvh' (a skeleton's motion — needs a rig) · 'export_png_sequence' (one file per frame)."),
|
|
4165
4504
|
options: z.record(z.string(), z.unknown()).optional().describe('Format options, passed through to the exporter: frame range, dimensions, fps, naming.'),
|
|
4166
4505
|
data: z.union([z.string(), z.record(z.string(), z.unknown())]).optional()
|
|
4167
|
-
.describe('import_lottie: the Lottie JSON — an object
|
|
4506
|
+
.describe('import_lottie: the Lottie JSON — an object or a JSON string. A URL or a file path is NOT fetched: read the file and pass its contents.'),
|
|
4168
4507
|
skeletonId: z.string().optional().describe('export_bvh: which skeleton to write. A BVH is one skeleton\'s motion, so this is not optional in practice.'),
|
|
4169
4508
|
})
|
|
4170
4509
|
.refine((v) => v.action !== 'import_lottie' || v.data !== undefined, { message: 'import_lottie requires data', path: ['data'] })
|
|
@@ -4260,7 +4599,7 @@ export const SoundInputSchema = z.object({
|
|
|
4260
4599
|
* pinepaper_motion — the generators' Animation knob, pointed at anything.
|
|
4261
4600
|
*
|
|
4262
4601
|
* The same engine every generator's own animation runs on, reachable for any
|
|
4263
|
-
* group or list of items. Two kinds: a GROUP motion (
|
|
4602
|
+
* group or list of items. Two kinds: a GROUP motion (driftX/Y, swayX/Y, rotate,
|
|
4264
4603
|
* pulse, wave, bounce) moves the target as one; a FIELD motion (ripple,
|
|
4265
4604
|
* breathe, undulate) sweeps a crest through the children from an origin, with
|
|
4266
4605
|
* a chosen waveform. The field kind is the one nothing else here can do.
|
|
@@ -4273,7 +4612,7 @@ export const MotionInputSchema = z.object({
|
|
|
4273
4612
|
action: z.enum(['list', 'apply']).describe("'list' every motion, waveform and origin the engine offers · 'apply' one to a target."),
|
|
4274
4613
|
itemId: z.string().optional().describe('apply: a group or single item to move.'),
|
|
4275
4614
|
itemIds: z.array(z.string()).optional().describe('apply: several items, wrapped in a new group that becomes the motion host. The members keep their own identity and ids.'),
|
|
4276
|
-
motion: z.string().optional().describe("apply: the motion name — a GROUP motion (
|
|
4615
|
+
motion: z.string().optional().describe("apply: the motion name — a GROUP motion (driftX, driftY, swayX, swayY, rotate, pulse, wave, bounce) moves the target as one; a FIELD motion (ripple, breathe, undulate) sweeps a crest through its children. Call 'list' for the engine's own set rather than guessing."),
|
|
4277
4616
|
speed: z.number().optional().describe('apply: cycles per second, roughly. Higher is faster.'),
|
|
4278
4617
|
intensity: z.number().optional().describe('apply: amplitude. 0.15 is the usual default elsewhere in this surface.'),
|
|
4279
4618
|
waveform: z.enum(['sine', 'triangle', 'square', 'sawtooth', 'spike']).optional()
|
|
@@ -4472,7 +4811,8 @@ export const RiggingInputSchema = z.object({
|
|
|
4472
4811
|
duration: z.number().positive().optional().describe('Seconds to traverse the path (default 1) — set_target_path.'),
|
|
4473
4812
|
loop: z.boolean().optional().describe('Cycle the path — set_target_path.'),
|
|
4474
4813
|
})
|
|
4475
|
-
|
|
4814
|
+
// Listing ALL skeletons or pose libraries needs no skeleton (gate D6, 1.6.19).
|
|
4815
|
+
.refine((v) => ['create_skeleton', 'import_bvh', 'import_spine', 'list_skeletons', 'list_pose_libraries'].includes(v.action) || !!v.skeletonId, { message: 'this action requires skeletonId', path: ['skeletonId'] })
|
|
4476
4816
|
.refine((v) => v.action !== 'add_bone' || v.skeletonId != null, { message: 'add_bone requires skeletonId', path: ['skeletonId'] })
|
|
4477
4817
|
.refine((v) => v.action !== 'attach_item' || (!!v.boneId && !!v.itemId), { message: 'attach_item requires boneId and itemId', path: ['boneId'] })
|
|
4478
4818
|
.refine((v) => v.action !== 'create_ik_chain' || (!!v.boneIds && v.boneIds.length >= 2), { message: 'create_ik_chain requires boneIds (≥2)', path: ['boneIds'] })
|