@pinepaper.studio/mcp-server 1.6.27 → 1.6.28
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.md +85 -2
- package/SECURITY.md +4 -3
- package/dist/build-info.json +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/ontology/upstream-vocabulary.d.ts +2 -2
- package/dist/ontology/upstream-vocabulary.js +2 -2
- package/dist/tools/choreograph.d.ts.map +1 -1
- package/dist/tools/choreograph.js +7 -0
- package/dist/tools/choreograph.js.map +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +35 -18
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/engine-lists.d.ts +11 -4
- package/dist/tools/engine-lists.d.ts.map +1 -1
- package/dist/tools/engine-lists.js +49 -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 +1 -0
- package/dist/tools/engine-methods.js.map +1 -1
- package/dist/tools/engine-surface.d.ts +1 -1
- package/dist/tools/engine-surface.d.ts.map +1 -1
- package/dist/tools/engine-surface.js +16 -4
- package/dist/tools/engine-surface.js.map +1 -1
- package/dist/tools/export-file-receipt.d.ts +21 -0
- package/dist/tools/export-file-receipt.d.ts.map +1 -1
- package/dist/tools/export-file-receipt.js +36 -13
- package/dist/tools/export-file-receipt.js.map +1 -1
- package/dist/tools/handlers/export.d.ts.map +1 -1
- package/dist/tools/handlers/export.js +6 -2
- package/dist/tools/handlers/export.js.map +1 -1
- package/dist/tools/handlers/generate.js +2 -2
- package/dist/tools/handlers/generate.js.map +1 -1
- package/dist/tools/handlers.d.ts +5 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +13 -0
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/lane.d.ts +7 -3
- package/dist/tools/lane.d.ts.map +1 -1
- package/dist/tools/lane.js +20 -5
- package/dist/tools/lane.js.map +1 -1
- package/dist/tools/scene-cache-export.d.ts.map +1 -1
- package/dist/tools/scene-cache-export.js +8 -4
- package/dist/tools/scene-cache-export.js.map +1 -1
- package/dist/tools/shape-params.d.ts +2 -2
- package/dist/tools/shape-params.js +2 -2
- package/dist/tools/stick-vocabulary.d.ts +1 -1
- package/dist/tools/stick-vocabulary.js +1 -1
- package/dist/tools/toolkits.d.ts +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +8 -2
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +103 -10
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +27 -2
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +7 -23
- package/dist/types/schemas.js.map +1 -1
- package/dist/utils/asset-paths.d.ts.map +1 -1
- package/dist/utils/asset-paths.js +11 -0
- package/dist/utils/asset-paths.js.map +1 -1
- package/dist/utils/capabilities.d.ts +15 -0
- package/dist/utils/capabilities.d.ts.map +1 -0
- package/dist/utils/capabilities.js +63 -0
- package/dist/utils/capabilities.js.map +1 -0
- package/dist/utils/env.d.ts.map +1 -1
- package/dist/utils/env.js +2 -1
- package/dist/utils/env.js.map +1 -1
- package/manifest.json +18 -2
- package/package.json +1 -1
|
@@ -1333,6 +1333,25 @@ function editDistance(a, b) {
|
|
|
1333
1333
|
return row[b.length];
|
|
1334
1334
|
}
|
|
1335
1335
|
export function relationOptionWarning(relationType, params, injected = []) {
|
|
1336
|
+
const flip = equationFlipNote(relationType, params);
|
|
1337
|
+
const unread = unreadOptionWarning(relationType, params, injected);
|
|
1338
|
+
return unread && flip ? `${unread} ${flip}` : unread ?? flip;
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* An equation route is in math coordinates, y up, unless flipY: false. Authored
|
|
1342
|
+
* as screen coordinates it rode the curve mirrored about cy, every centroid
|
|
1343
|
+
* within a pixel of the mirror and up to 300 px off the curve as written, with
|
|
1344
|
+
* nothing said (ORBDUR26, ASYMORB27). Said when the caller left flipY to the default.
|
|
1345
|
+
*/
|
|
1346
|
+
function equationFlipNote(relationType, params) {
|
|
1347
|
+
if (relationType !== 'moves_along_path' || !params || typeof params !== 'object')
|
|
1348
|
+
return null;
|
|
1349
|
+
const eq = params.equation;
|
|
1350
|
+
if (!eq || typeof eq !== 'object' || eq.flipY !== undefined)
|
|
1351
|
+
return null;
|
|
1352
|
+
return 'params.equation is read with y UP (flipY defaults to true), so on screen the curve is mirrored about cy. If you wrote it in screen coordinates (y down), add flipY: false to the equation.';
|
|
1353
|
+
}
|
|
1354
|
+
function unreadOptionWarning(relationType, params, injected) {
|
|
1336
1355
|
const keys = typeof relationType === 'string' ? RELATION_PARAM_KEYS[relationType] : undefined;
|
|
1337
1356
|
if (!keys || !params || typeof params !== 'object')
|
|
1338
1357
|
return null;
|
|
@@ -1710,10 +1729,18 @@ const relations = results.map(r => ({
|
|
|
1710
1729
|
/**
|
|
1711
1730
|
* Template for simple animation
|
|
1712
1731
|
*/
|
|
1713
|
-
function generateAnimateCode(itemId, animationType, speed, intensity, delay) {
|
|
1732
|
+
function generateAnimateCode(itemId, animationType, speed, intensity, delay, waveform, origin) {
|
|
1714
1733
|
const ID = jsLiteral(itemId); // caller text: a quoted literal, never spliced raw
|
|
1715
1734
|
const extra = (intensity !== undefined ? `,\n animationIntensity: ${intensity}` : '') +
|
|
1716
|
-
(delay !== undefined ? `,\n animationDelay: ${delay}` : '')
|
|
1735
|
+
(delay !== undefined ? `,\n animationDelay: ${delay}` : '') +
|
|
1736
|
+
(waveform !== undefined ? `,\n animationWaveform: ${jsLiteral(waveform)}` : '') +
|
|
1737
|
+
(origin !== undefined ? `,\n animationOrigin: ${jsLiteral(origin)}` : '');
|
|
1738
|
+
// A studio from before field motions (FxTool #255) drops waveform and origin
|
|
1739
|
+
// without a word, and the wave runs as sine from the centre. Read them back.
|
|
1740
|
+
const optionCheck = waveform === undefined && origin === undefined ? '' : `
|
|
1741
|
+
const __unset = [${waveform !== undefined ? `['waveform', 'animationWaveform', ${jsLiteral(waveform)}]` : ''}${waveform !== undefined && origin !== undefined ? ', ' : ''}${origin !== undefined ? `['origin', 'animationOrigin', ${jsLiteral(origin)}]` : ''}]
|
|
1742
|
+
.filter(function (o) { return !item.data || item.data[o[1]] !== o[2]; }).map(function (o) { return o[0]; });
|
|
1743
|
+
if (__unset.length) out.warning = __unset.join(' and ') + ' did not take: this studio predates field-motion options (or refused the value), so the wave runs as sine from the centre. Update the studio.';`;
|
|
1717
1744
|
return `
|
|
1718
1745
|
// Apply ${oneLine(animationType)} animation to ${ID}
|
|
1719
1746
|
const item = app.getItemById(${ID});
|
|
@@ -1740,7 +1767,8 @@ app.animate(item, {
|
|
|
1740
1767
|
known: r.known,
|
|
1741
1768
|
};
|
|
1742
1769
|
}
|
|
1743
|
-
|
|
1770
|
+
const out = { success: true, itemId: ${ID}, animationType: ${jsLiteral(animationType)} };${optionCheck}
|
|
1771
|
+
return out;
|
|
1744
1772
|
})();
|
|
1745
1773
|
`.trim();
|
|
1746
1774
|
}
|
|
@@ -1816,6 +1844,12 @@ const TEXT_KEY_ENGINE_NAMES = {
|
|
|
1816
1844
|
textAlign: ['textAlign', 'justification'],
|
|
1817
1845
|
lineHeight: ['lineHeight', 'leading'],
|
|
1818
1846
|
letterSpacing: ['letterSpacing'],
|
|
1847
|
+
// Text style keys no studio has animated so far. Keyed, they were stored and
|
|
1848
|
+
// drew nothing, with success:true (WORDSPC26). Checked against the same list,
|
|
1849
|
+
// so a studio that learns one is believed and the warning stops.
|
|
1850
|
+
wordSpacing: ['wordSpacing'],
|
|
1851
|
+
textDecoration: ['textDecoration'],
|
|
1852
|
+
textTransform: ['textTransform'],
|
|
1819
1853
|
};
|
|
1820
1854
|
/** A JS expression (run in the page) → the keys of `keys` this studio does not animate. '' when none are text keys. */
|
|
1821
1855
|
function unanimatedTextKeysJs(keys) {
|
|
@@ -1830,7 +1864,7 @@ function unanimatedTextKeysJs(keys) {
|
|
|
1830
1864
|
})()`;
|
|
1831
1865
|
}
|
|
1832
1866
|
/** The sentence for the keys unanimatedTextKeysJs found, built in the page. */
|
|
1833
|
-
const UNANIMATED_TEXT_KEYS_SENTENCE = `' is not an animatable property in this studio (app.listAnimatableProperties does not list it), so it had no effect
|
|
1867
|
+
const UNANIMATED_TEXT_KEYS_SENTENCE = `' is not an animatable property in this studio (app.listAnimatableProperties does not list it), so it had no effect: a text key animates only where that list names it. Here, set a fixed value with pinepaper_modify_item, or animate fontSize or scaleX.'`;
|
|
1834
1868
|
function generateKeyframeAnimateCode(itemId, keyframes, duration, loop = false, timeOffset, clipInPoint, clipOutPoint, timeUnits, append = false, easingAt) {
|
|
1835
1869
|
const ID = jsLiteral(itemId); // caller text: a quoted literal, never spliced raw
|
|
1836
1870
|
const keyframesJson = JSON.stringify(normalizeKeyframePositions(keyframes), null, 2);
|
|
@@ -3282,7 +3316,7 @@ export class PinePaperCodeGenerator {
|
|
|
3282
3316
|
*/
|
|
3283
3317
|
generateAnimate(input) {
|
|
3284
3318
|
const validated = AnimateItemInputSchema.parse(input);
|
|
3285
|
-
return generateAnimateCode(validated.itemId, validated.animationType, validated.speed, validated.intensity, validated.delay);
|
|
3319
|
+
return generateAnimateCode(validated.itemId, validated.animationType, validated.speed, validated.intensity, validated.delay, validated.waveform, validated.origin);
|
|
3286
3320
|
}
|
|
3287
3321
|
/**
|
|
3288
3322
|
* Generate code for keyframe animation
|
|
@@ -4878,7 +4912,24 @@ return __created;
|
|
|
4878
4912
|
// The emitter embeds the id as a literal; a batch ref ($N) is only known
|
|
4879
4913
|
// at run time, so it is generated with a placeholder and the runtime
|
|
4880
4914
|
// variable put in its place.
|
|
4881
|
-
|
|
4915
|
+
// A TOP-LEVEL position moves the item, as it places one on create (#42: it reported modified:true and the
|
|
4916
|
+
// item stayed put; only properties were read). properties.x / y / position, when given, win.
|
|
4917
|
+
const modProps = { ...(op.properties || {}) };
|
|
4918
|
+
const pos = op.position;
|
|
4919
|
+
const hasOwn = modProps.x !== undefined || modProps.y !== undefined || modProps.position !== undefined;
|
|
4920
|
+
if (pos !== undefined && !hasOwn) {
|
|
4921
|
+
const [px, py] = Array.isArray(pos) ? pos : [pos.x, pos.y];
|
|
4922
|
+
if (typeof px === 'number')
|
|
4923
|
+
modProps.x = px;
|
|
4924
|
+
if (typeof py === 'number')
|
|
4925
|
+
modProps.y = py;
|
|
4926
|
+
}
|
|
4927
|
+
if (!Object.keys(modProps).length) {
|
|
4928
|
+
return `
|
|
4929
|
+
return { success: false, itemId: ${itemRef}, error: 'modify changes nothing: pass properties ({"x": 700}, {"fillColor": "#f00"}) or a top-level position.' };
|
|
4930
|
+
`;
|
|
4931
|
+
}
|
|
4932
|
+
const single = generateModifyItemCode('__MID__', modProps).trim()
|
|
4882
4933
|
.replace(/"__MID__"/g, '__mid')
|
|
4883
4934
|
.replace(/__MID__/g, "' + __mid + '")
|
|
4884
4935
|
.replace(/;\s*$/, '');
|
|
@@ -4940,7 +4991,7 @@ const targetId = ${noTarget ? 'null' : tgtRef};
|
|
|
4940
4991
|
const target = ${noTarget ? 'null' : 'app.getItemById(targetId)'};
|
|
4941
4992
|
${noTarget ? '' : "if (!target) throw new Error('Target not found: ' + targetId);\n"}${op.update === true ? "if (typeof app.modifyRelationParams !== 'function') throw new Error('this studio cannot change a bound relation (app.modifyRelationParams): update PinePaper Studio');\n" : ''}const __done = [], __failed = [];
|
|
4942
4993
|
__members.forEach((m, i) => {
|
|
4943
|
-
const __p = Object.assign({}, ${relOpts}${glob ?
|
|
4994
|
+
const __p = Object.assign({}, ${relOpts}${glob ? `, { index: i, count: __members.length, pattern: ${jsLiteral(op.sourceId)} }` : ''});
|
|
4944
4995
|
const ok = ${op.update === true
|
|
4945
4996
|
? `app.modifyRelationParams(m.id, targetId, ${type}, __p) || (targetId === null && app.modifyRelationParams(m.id, undefined, ${type}, __p))`
|
|
4946
4997
|
: glob
|
|
@@ -4955,7 +5006,7 @@ if (!__done.length) {
|
|
|
4955
5006
|
const __why = ${op.update === true ? `'no ' + ${type} + ' relation is bound from ' + __failed.join(', ') + (targetId === null ? '' : ' to ' + targetId) + ' to change: bind it first (a relation op without update)'` : relationRefusalReasonJs(type)};
|
|
4956
5007
|
return { success: false, relationType: ${type}, sourceIds: __failed, error: ${op.update === true ? '__why' : `'the studio refused the ' + ${type} + ' relation for ' + __failed.join(', ') + ' — nothing was bound' + (__why ? ': ' + __why : '.')`} };
|
|
4957
5008
|
}
|
|
4958
|
-
return { success: true, relationType: ${type}, ${op.update === true ? 'updated' : 'bound'}: __done, targetId${glob ? ', count: __members.length' : ''}${(() => { const w = relationOptionWarning(op.relationType, op.relationOptions, ['index', 'count']); return w ? `, optionWarning: ${jsLiteral(w)}` : ''; })()}, ...(__failed.length ? { failed: __failed, warning: __failed.length + ' of ' + __members.length + ' were not ${op.update === true ? 'changed' : 'bound'}: ' + __failed.join(', ') } : {}) };
|
|
5009
|
+
return { success: true, relationType: ${type}, ${op.update === true ? 'updated' : 'bound'}: __done, targetId${glob ? ', count: __members.length' : ''}${(() => { const w = relationOptionWarning(op.relationType, op.relationOptions, ['index', 'count', 'pattern']); return w ? `, optionWarning: ${jsLiteral(w)}` : ''; })()}, ...(__failed.length ? { failed: __failed, warning: __failed.length + ' of ' + __members.length + ' were not ${op.update === true ? 'changed' : 'bound'}: ' + __failed.join(', ') } : {}) };
|
|
4959
5010
|
`;
|
|
4960
5011
|
}
|
|
4961
5012
|
return `
|
|
@@ -9124,8 +9175,17 @@ ${input.cutSound !== undefined ? ` const __cuts = (__cr && __cr.cuts) || (app.s
|
|
|
9124
9175
|
}
|
|
9125
9176
|
case 'modify': {
|
|
9126
9177
|
const ch = { ...(v.params ? { params: v.params } : {}), ...(v.members ? { members: v.members } : {}), ...(v.window !== undefined ? { window: v.window } : {}) };
|
|
9178
|
+
// A pattern made with members as a plain array is stored as { refs }, a
|
|
9179
|
+
// shape the engine's own re-check does not accept, so a params-only
|
|
9180
|
+
// modify of it failed with "members is …" (PATIMG24). The stored refs
|
|
9181
|
+
// are handed back as the array they came from.
|
|
9182
|
+
const keep = v.members ? '' : `
|
|
9183
|
+
const __cur = typeof app.getPattern === 'function' ? app.getPattern(${S(v.id)}) : null;
|
|
9184
|
+
const __m = __cur && __cur.spec && __cur.spec.members;
|
|
9185
|
+
if (__m && Array.isArray(__m.refs)) __ch.members = __m.refs.slice();`;
|
|
9127
9186
|
body = `${need('modifyPattern')}
|
|
9128
|
-
const
|
|
9187
|
+
const __ch = ${S(ch)};${keep}
|
|
9188
|
+
const r = app.modifyPattern(${S(v.id)}, __ch);
|
|
9129
9189
|
return Object.assign({ success: !!(r && r.ok) }, r || {}, app.lastReceipt ? { receipt: app.lastReceipt } : {});`;
|
|
9130
9190
|
break;
|
|
9131
9191
|
}
|
|
@@ -11982,12 +12042,45 @@ ${checks.includes('contrast') ? ` // SAMPLED OVER TIME (round 12, 12.9): text i
|
|
|
11982
12042
|
}));
|
|
11983
12043
|
} catch (e) { /* the relations are an addition; the lint stands without them */ }
|
|
11984
12044
|
const __custom = (typeof window !== 'undefined' && window.__ppCustomRelations) ? Object.values(window.__ppCustomRelations) : [];
|
|
12045
|
+
// HOW TO CHANGE IT (#43): an editing agent needs the op, not the schemas. Each relation comes with the batch op that
|
|
12046
|
+
// changes it; a group bound by a name pattern ("dot-*", recorded in each member's params) comes once, with the ops
|
|
12047
|
+
// that add and remove a member; a custom motion with the call that redefines it.
|
|
12048
|
+
const __edit = { hint: 'To change this scene, send the ops below (your values in relationOptions) in ONE pinepaper_agent_batch_execute call; a custom motion changes by sending its pinepaper_register_custom_relation call again.', groups: [], relations: [], customRelations: [] };
|
|
12049
|
+
try {
|
|
12050
|
+
const __byPat = new Map();
|
|
12051
|
+
__rels.forEach((r) => {
|
|
12052
|
+
const pat = r.params && r.params.pattern;
|
|
12053
|
+
if (typeof pat === 'string') {
|
|
12054
|
+
const k = pat + '|' + r.type + '|' + r.to;
|
|
12055
|
+
if (!__byPat.has(k)) __byPat.set(k, { pattern: pat, type: r.type, to: r.to, members: [] });
|
|
12056
|
+
__byPat.get(k).members.push(r);
|
|
12057
|
+
return;
|
|
12058
|
+
}
|
|
12059
|
+
__edit.relations.push({ from: r.from, ...(r.fromName ? { fromName: r.fromName } : {}), to: r.to, relationType: r.type,
|
|
12060
|
+
change: { type: 'relation', sourceId: r.fromName || r.from, ...(r.to != null ? { targetId: r.to } : {}), relationType: r.type, update: true, relationOptions: r.params } });
|
|
12061
|
+
});
|
|
12062
|
+
__byPat.forEach((g) => {
|
|
12063
|
+
const base = Object.assign({}, g.members[0].params); delete base.index; delete base.count; delete base.pattern;
|
|
12064
|
+
const names = g.members.map((m) => m.fromName).filter(Boolean).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
|
|
12065
|
+
const prefix = g.pattern.split('*')[0];
|
|
12066
|
+
const nums = names.map((n) => Number(n.slice(prefix.length))).filter((n) => Number.isFinite(n));
|
|
12067
|
+
const next = prefix + (nums.length ? Math.max.apply(null, nums) + 1 : g.members.length + 1);
|
|
12068
|
+
const firstEntry = app.itemRegistry.get(g.members[0].from);
|
|
12069
|
+
const bind = { type: 'relation', sourceId: g.pattern, ...(g.to != null ? { targetId: g.to } : {}), relationType: g.type, relationOptions: base };
|
|
12070
|
+
__edit.groups.push({ pattern: g.pattern, relationType: g.type, members: g.members.length, ...(names.length ? { first: names[0], last: names[names.length - 1] } : {}), relationOptions: base,
|
|
12071
|
+
change: Object.assign({}, bind, { update: true }),
|
|
12072
|
+
addMember: [{ type: 'create', itemType: (firstEntry && firstEntry.type) || 'circle', position: [0, 0], properties: { name: next } }, bind],
|
|
12073
|
+
...(names.length ? { removeMember: [{ type: 'delete', itemId: names[names.length - 1] }, bind] } : {}) });
|
|
12074
|
+
});
|
|
12075
|
+
__custom.forEach((d) => __edit.customRelations.push({ name: d.name, change: { tool: 'pinepaper_register_custom_relation', args: d } }));
|
|
12076
|
+
} catch (e) { /* the read-back stands without the edit ops */ }
|
|
11985
12077
|
// warnings: what the engine's own rules found (TEXT_CLIPS_FRAME: a centre-anchored text running past the
|
|
11986
12078
|
// frame edge). Passed through when the studio reports them; an older studio has none to pass.
|
|
11987
12079
|
return { success: true, density: result.density, suggestions: result.suggestions,
|
|
11988
12080
|
...(Array.isArray(result.warnings) && result.warnings.length ? { warnings: result.warnings } : {}),
|
|
11989
12081
|
relations: __rels, ...(__relCount > __rels.length ? { relationsShown: __rels.length, relationCount: __relCount } : {}),
|
|
11990
|
-
...(__custom.length ? { customRelations: __custom } : {})
|
|
12082
|
+
...(__custom.length ? { customRelations: __custom } : {}),
|
|
12083
|
+
...(__edit.groups.length || __edit.relations.length || __edit.customRelations.length ? { edit: __edit } : {}) };
|
|
11991
12084
|
})();`.trim();
|
|
11992
12085
|
}
|
|
11993
12086
|
/**
|