@efectoapp/mcp-server 0.1.23 → 0.1.24
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 +7 -6
- package/dist/tools/output.d.ts.map +1 -1
- package/dist/tools/output.js +45 -8
- package/dist/tools/output.js.map +1 -1
- package/dist/tools/render.d.ts.map +1 -1
- package/dist/tools/render.js +7 -12
- package/dist/tools/render.js.map +1 -1
- package/dist/tools/state.d.ts +0 -5
- package/dist/tools/state.d.ts.map +1 -1
- package/dist/tools/state.js +115 -252
- package/dist/tools/state.js.map +1 -1
- package/package.json +1 -1
package/dist/tools/state.js
CHANGED
|
@@ -14,13 +14,9 @@
|
|
|
14
14
|
*/
|
|
15
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
16
|
exports.stateTools = void 0;
|
|
17
|
-
exports.ensurePoster = ensurePoster;
|
|
18
17
|
exports.resetState = resetState;
|
|
19
18
|
exports.getCurrentState = getCurrentState;
|
|
20
19
|
exports.handleStateTool = handleStateTool;
|
|
21
|
-
const os_1 = require("os");
|
|
22
|
-
const fs_1 = require("fs");
|
|
23
|
-
const path_1 = require("path");
|
|
24
20
|
// Helper to clamp values to valid ranges
|
|
25
21
|
function clamp(value, min, max, fallback) {
|
|
26
22
|
const v = value ?? fallback;
|
|
@@ -399,75 +395,9 @@ function normalizeFontFamily(input) {
|
|
|
399
395
|
}
|
|
400
396
|
// Current poster state (single poster for now)
|
|
401
397
|
let currentPoster = null;
|
|
402
|
-
// --- File-based state persistence ---
|
|
403
|
-
// Some MCP clients spawn a fresh process per tool call, losing in-memory state.
|
|
404
|
-
// We persist to a temp file so state survives process restarts.
|
|
405
|
-
const STATE_FILE_PATH = (0, path_1.join)((0, os_1.tmpdir)(), 'efecto-mcp-state.json');
|
|
406
|
-
const STATE_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
|
|
407
|
-
/** Write current poster state to the temp file (best-effort). */
|
|
408
|
-
function persistState() {
|
|
409
|
-
if (!currentPoster)
|
|
410
|
-
return;
|
|
411
|
-
try {
|
|
412
|
-
const payload = JSON.stringify({ timestamp: Date.now(), state: currentPoster });
|
|
413
|
-
(0, fs_1.writeFileSync)(STATE_FILE_PATH, payload, 'utf-8');
|
|
414
|
-
}
|
|
415
|
-
catch {
|
|
416
|
-
// Silently ignore — falls back to in-memory only
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
/** Load persisted state from the temp file if valid and not stale. */
|
|
420
|
-
function loadPersistedState() {
|
|
421
|
-
try {
|
|
422
|
-
const raw = (0, fs_1.readFileSync)(STATE_FILE_PATH, 'utf-8');
|
|
423
|
-
const parsed = JSON.parse(raw);
|
|
424
|
-
if (!parsed.state || !parsed.timestamp)
|
|
425
|
-
return null;
|
|
426
|
-
if (Date.now() - parsed.timestamp > STATE_MAX_AGE_MS)
|
|
427
|
-
return null;
|
|
428
|
-
return parsed.state;
|
|
429
|
-
}
|
|
430
|
-
catch {
|
|
431
|
-
return null;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
/**
|
|
435
|
-
* Ensure a poster exists. Restores from persisted file if needed,
|
|
436
|
-
* or auto-creates a default poster as a last resort.
|
|
437
|
-
*/
|
|
438
|
-
function ensurePoster() {
|
|
439
|
-
if (currentPoster)
|
|
440
|
-
return currentPoster;
|
|
441
|
-
// Try restoring from persisted state
|
|
442
|
-
const persisted = loadPersistedState();
|
|
443
|
-
if (persisted) {
|
|
444
|
-
currentPoster = persisted;
|
|
445
|
-
return currentPoster;
|
|
446
|
-
}
|
|
447
|
-
// Auto-create a default poster (same defaults as create_poster)
|
|
448
|
-
currentPoster = {
|
|
449
|
-
canvas: {
|
|
450
|
-
aspectRatio: '9:16',
|
|
451
|
-
backgroundColor: '#1a1a1a',
|
|
452
|
-
layoutMode: 'column',
|
|
453
|
-
alignItems: 'center',
|
|
454
|
-
justifyContent: 'start',
|
|
455
|
-
gap: 0.04,
|
|
456
|
-
padding: { top: 0.08, right: 0.08, bottom: 0.08, left: 0.08 },
|
|
457
|
-
},
|
|
458
|
-
layers: [createDefaultBackgroundLayer('#1a1a1a')],
|
|
459
|
-
postProcesses: [],
|
|
460
|
-
};
|
|
461
|
-
persistState();
|
|
462
|
-
return currentPoster;
|
|
463
|
-
}
|
|
464
398
|
// Reset state (useful for testing)
|
|
465
399
|
function resetState() {
|
|
466
400
|
currentPoster = null;
|
|
467
|
-
try {
|
|
468
|
-
(0, fs_1.unlinkSync)(STATE_FILE_PATH);
|
|
469
|
-
}
|
|
470
|
-
catch { /* ignore */ }
|
|
471
401
|
}
|
|
472
402
|
// Get current state
|
|
473
403
|
function getCurrentState() {
|
|
@@ -800,113 +730,6 @@ function parseGradientStopsInput(input, fallbackStops) {
|
|
|
800
730
|
.filter((stop) => stop !== null);
|
|
801
731
|
return parsed.length >= MIN_GRADIENT_STOPS ? parsed : null;
|
|
802
732
|
}
|
|
803
|
-
/**
|
|
804
|
-
* Build a Fill object from flat MCP tool params (fillType, gradientStops, etc.).
|
|
805
|
-
* Returns null when no gradient params are present (caller should fall back to solid).
|
|
806
|
-
*/
|
|
807
|
-
function buildFillFromParams(params) {
|
|
808
|
-
const fillType = params.fillType;
|
|
809
|
-
const hasGradientParams = fillType === 'linear' || fillType === 'radial' ||
|
|
810
|
-
params.gradientStyle !== undefined ||
|
|
811
|
-
params.gradientStops !== undefined ||
|
|
812
|
-
params.gradientStartColor !== undefined ||
|
|
813
|
-
params.gradientEndColor !== undefined;
|
|
814
|
-
if (!hasGradientParams)
|
|
815
|
-
return null;
|
|
816
|
-
const gradientStyle = fillType === 'radial' || params.gradientStyle === 'radial' ? 'radial' : 'linear';
|
|
817
|
-
const defaults = gradientStyle === 'radial'
|
|
818
|
-
? DEFAULT_RADIAL_GRADIENT_FILL
|
|
819
|
-
: DEFAULT_LINEAR_GRADIENT_FILL;
|
|
820
|
-
const explicitStops = parseGradientStopsInput(params.gradientStops, defaults.stops);
|
|
821
|
-
const stops = explicitStops ?? [
|
|
822
|
-
{
|
|
823
|
-
color: params.gradientStartColor || defaults.stops[0].color,
|
|
824
|
-
opacity: defaults.stops[0].opacity,
|
|
825
|
-
position: defaults.stops[0].position,
|
|
826
|
-
},
|
|
827
|
-
{
|
|
828
|
-
color: params.gradientEndColor || defaults.stops[1].color,
|
|
829
|
-
opacity: defaults.stops[1].opacity,
|
|
830
|
-
position: defaults.stops[1].position,
|
|
831
|
-
},
|
|
832
|
-
];
|
|
833
|
-
if (gradientStyle === 'radial') {
|
|
834
|
-
return normalizeRadialGradientFill({
|
|
835
|
-
type: 'radial',
|
|
836
|
-
center: {
|
|
837
|
-
x: Number.isFinite(params.gradientCenterX)
|
|
838
|
-
? params.gradientCenterX
|
|
839
|
-
: DEFAULT_RADIAL_GRADIENT_FILL.center.x,
|
|
840
|
-
y: Number.isFinite(params.gradientCenterY)
|
|
841
|
-
? params.gradientCenterY
|
|
842
|
-
: DEFAULT_RADIAL_GRADIENT_FILL.center.y,
|
|
843
|
-
},
|
|
844
|
-
radius: Number.isFinite(params.gradientRadius)
|
|
845
|
-
? params.gradientRadius
|
|
846
|
-
: DEFAULT_RADIAL_GRADIENT_FILL.radius,
|
|
847
|
-
stops,
|
|
848
|
-
});
|
|
849
|
-
}
|
|
850
|
-
return normalizeLinearGradientFill({
|
|
851
|
-
type: 'linear',
|
|
852
|
-
angle: Number.isFinite(params.gradientAngle)
|
|
853
|
-
? params.gradientAngle
|
|
854
|
-
: DEFAULT_LINEAR_GRADIENT_FILL.angle,
|
|
855
|
-
stops,
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
/** Reusable schema properties for gradient fill params — spread into tool schemas. */
|
|
859
|
-
const GRADIENT_FILL_SCHEMA_PROPS = {
|
|
860
|
-
fillType: {
|
|
861
|
-
type: 'string',
|
|
862
|
-
enum: ['solid', 'linear', 'radial'],
|
|
863
|
-
description: 'Fill type: "solid" (default), "linear" gradient, or "radial" gradient',
|
|
864
|
-
},
|
|
865
|
-
gradientStops: {
|
|
866
|
-
type: 'array',
|
|
867
|
-
description: 'Gradient color stops (2-8). Overrides start/end colors when provided.',
|
|
868
|
-
items: {
|
|
869
|
-
type: 'object',
|
|
870
|
-
properties: {
|
|
871
|
-
color: { type: 'string', description: 'Stop color in hex' },
|
|
872
|
-
opacity: { type: 'number', description: 'Stop opacity (0-1)' },
|
|
873
|
-
position: { type: 'number', description: 'Stop position (0-1)' },
|
|
874
|
-
},
|
|
875
|
-
required: ['color', 'position'],
|
|
876
|
-
},
|
|
877
|
-
minItems: 2,
|
|
878
|
-
maxItems: 8,
|
|
879
|
-
},
|
|
880
|
-
gradientAngle: {
|
|
881
|
-
type: 'number',
|
|
882
|
-
description: 'Linear gradient angle in degrees (default 135)',
|
|
883
|
-
},
|
|
884
|
-
gradientStartColor: {
|
|
885
|
-
type: 'string',
|
|
886
|
-
description: 'Gradient start color in hex (shortcut for 2-stop gradient)',
|
|
887
|
-
},
|
|
888
|
-
gradientEndColor: {
|
|
889
|
-
type: 'string',
|
|
890
|
-
description: 'Gradient end color in hex (shortcut for 2-stop gradient)',
|
|
891
|
-
},
|
|
892
|
-
gradientStyle: {
|
|
893
|
-
type: 'string',
|
|
894
|
-
enum: ['linear', 'radial'],
|
|
895
|
-
description: 'Gradient style (alternative to fillType for setting gradient type)',
|
|
896
|
-
},
|
|
897
|
-
gradientCenterX: {
|
|
898
|
-
type: 'number',
|
|
899
|
-
description: 'Radial gradient center X (0-1)',
|
|
900
|
-
},
|
|
901
|
-
gradientCenterY: {
|
|
902
|
-
type: 'number',
|
|
903
|
-
description: 'Radial gradient center Y (0-1)',
|
|
904
|
-
},
|
|
905
|
-
gradientRadius: {
|
|
906
|
-
type: 'number',
|
|
907
|
-
description: 'Radial gradient radius (0.05-2)',
|
|
908
|
-
},
|
|
909
|
-
};
|
|
910
733
|
// Tool definitions
|
|
911
734
|
exports.stateTools = [
|
|
912
735
|
{
|
|
@@ -1115,7 +938,6 @@ exports.stateTools = [
|
|
|
1115
938
|
// Shape style properties (rectangle/ellipse/polygon/star/line)
|
|
1116
939
|
fillColor: { type: 'string', description: 'Shape fill color in hex (e.g. #ffffff)' },
|
|
1117
940
|
fillOpacity: { type: 'number', description: 'Shape fill opacity (0-1)' },
|
|
1118
|
-
...GRADIENT_FILL_SCHEMA_PROPS,
|
|
1119
941
|
strokeColor: { type: 'string', description: 'Shape stroke color in hex' },
|
|
1120
942
|
strokeWidth: { type: 'number', description: 'Shape stroke width (normalized; e.g. 0.005-0.02)' },
|
|
1121
943
|
strokeOpacity: { type: 'number', description: 'Shape stroke opacity (0-1)' },
|
|
@@ -1324,7 +1146,6 @@ exports.stateTools = [
|
|
|
1324
1146
|
// Shape style
|
|
1325
1147
|
fillColor: { type: 'string', description: 'Shape fill color (hex)' },
|
|
1326
1148
|
fillOpacity: { type: 'number', description: 'Shape fill opacity (0-1)' },
|
|
1327
|
-
...GRADIENT_FILL_SCHEMA_PROPS,
|
|
1328
1149
|
strokeColor: { type: 'string', description: 'Shape stroke color (hex)' },
|
|
1329
1150
|
strokeWidth: { type: 'number', description: 'Shape stroke width (normalized; e.g. 0.005-0.02)' },
|
|
1330
1151
|
strokeOpacity: { type: 'number', description: 'Shape stroke opacity (0-1)' },
|
|
@@ -1496,7 +1317,6 @@ exports.stateTools = [
|
|
|
1496
1317
|
paddingLeft: { type: 'number', description: 'Padding left as % of canvas' },
|
|
1497
1318
|
fillColor: { type: 'string', description: 'Fill color (group background OR shape fill)' },
|
|
1498
1319
|
fillOpacity: { type: 'number', description: 'Fill opacity (group background OR shape fill)' },
|
|
1499
|
-
...GRADIENT_FILL_SCHEMA_PROPS,
|
|
1500
1320
|
cornerRadius: { type: 'number', description: 'Corner radius (group OR shape)' },
|
|
1501
1321
|
// Shadow properties (all layer types)
|
|
1502
1322
|
shadowOffsetX: { type: 'number', description: 'Shadow horizontal offset in CSS px' },
|
|
@@ -1769,7 +1589,6 @@ async function handleStateTool(name, args) {
|
|
|
1769
1589
|
layers: [createDefaultBackgroundLayer(backgroundColor)],
|
|
1770
1590
|
postProcesses: [],
|
|
1771
1591
|
};
|
|
1772
|
-
persistState();
|
|
1773
1592
|
return {
|
|
1774
1593
|
content: [
|
|
1775
1594
|
{
|
|
@@ -1785,7 +1604,11 @@ async function handleStateTool(name, args) {
|
|
|
1785
1604
|
};
|
|
1786
1605
|
}
|
|
1787
1606
|
case 'clear_poster': {
|
|
1788
|
-
|
|
1607
|
+
if (!currentPoster) {
|
|
1608
|
+
return {
|
|
1609
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1789
1612
|
const bgLayer = currentPoster.layers.find((l) => l.type === 'background');
|
|
1790
1613
|
const defaultBg = createDefaultBackgroundLayer(currentPoster.canvas.backgroundColor || '#1a1a1a');
|
|
1791
1614
|
currentPoster = {
|
|
@@ -1793,7 +1616,6 @@ async function handleStateTool(name, args) {
|
|
|
1793
1616
|
layers: bgLayer ? [bgLayer] : [defaultBg],
|
|
1794
1617
|
postProcesses: [],
|
|
1795
1618
|
};
|
|
1796
|
-
persistState();
|
|
1797
1619
|
return {
|
|
1798
1620
|
content: [{
|
|
1799
1621
|
type: 'text',
|
|
@@ -1805,7 +1627,11 @@ async function handleStateTool(name, args) {
|
|
|
1805
1627
|
};
|
|
1806
1628
|
}
|
|
1807
1629
|
case 'set_page_layout': {
|
|
1808
|
-
|
|
1630
|
+
if (!currentPoster) {
|
|
1631
|
+
return {
|
|
1632
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1809
1635
|
const layoutMode = params.layoutMode;
|
|
1810
1636
|
if (layoutMode !== undefined) {
|
|
1811
1637
|
if (layoutMode !== 'absolute' && layoutMode !== 'row' && layoutMode !== 'column') {
|
|
@@ -1843,7 +1669,6 @@ async function handleStateTool(name, args) {
|
|
|
1843
1669
|
};
|
|
1844
1670
|
currentPoster.canvas.padding = nextPadding;
|
|
1845
1671
|
}
|
|
1846
|
-
persistState();
|
|
1847
1672
|
return {
|
|
1848
1673
|
content: [
|
|
1849
1674
|
{
|
|
@@ -1858,7 +1683,11 @@ async function handleStateTool(name, args) {
|
|
|
1858
1683
|
};
|
|
1859
1684
|
}
|
|
1860
1685
|
case 'modify_canvas': {
|
|
1861
|
-
|
|
1686
|
+
if (!currentPoster) {
|
|
1687
|
+
return {
|
|
1688
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1862
1691
|
const canvas = currentPoster.canvas;
|
|
1863
1692
|
if (params.aspectRatio)
|
|
1864
1693
|
canvas.aspectRatio = params.aspectRatio;
|
|
@@ -1868,7 +1697,6 @@ async function handleStateTool(name, args) {
|
|
|
1868
1697
|
canvas.canvasColor = params.canvasColor;
|
|
1869
1698
|
if (params.overflow !== undefined)
|
|
1870
1699
|
canvas.overflow = params.overflow;
|
|
1871
|
-
persistState();
|
|
1872
1700
|
return {
|
|
1873
1701
|
content: [
|
|
1874
1702
|
{
|
|
@@ -1887,7 +1715,11 @@ async function handleStateTool(name, args) {
|
|
|
1887
1715
|
};
|
|
1888
1716
|
}
|
|
1889
1717
|
case 'set_background': {
|
|
1890
|
-
|
|
1718
|
+
if (!currentPoster) {
|
|
1719
|
+
return {
|
|
1720
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1891
1723
|
// Background is always at index 0
|
|
1892
1724
|
const bgLayer = currentPoster.layers[0];
|
|
1893
1725
|
if (bgLayer.type !== 'background') {
|
|
@@ -1899,8 +1731,53 @@ async function handleStateTool(name, args) {
|
|
|
1899
1731
|
const contentType = requestedType === 'gradient' ? 'solid' : requestedType;
|
|
1900
1732
|
bgLayer.contentType = contentType;
|
|
1901
1733
|
if (requestedType === 'gradient') {
|
|
1902
|
-
const
|
|
1903
|
-
|
|
1734
|
+
const gradientStyle = params.gradientStyle === 'radial' ? 'radial' : 'linear';
|
|
1735
|
+
const explicitStops = parseGradientStopsInput(params.gradientStops, gradientStyle === 'radial' ? DEFAULT_RADIAL_GRADIENT_FILL.stops : DEFAULT_LINEAR_GRADIENT_FILL.stops);
|
|
1736
|
+
const gradient = gradientStyle === 'radial'
|
|
1737
|
+
? normalizeRadialGradientFill({
|
|
1738
|
+
type: 'radial',
|
|
1739
|
+
center: {
|
|
1740
|
+
x: Number.isFinite(params.gradientCenterX)
|
|
1741
|
+
? params.gradientCenterX
|
|
1742
|
+
: DEFAULT_RADIAL_GRADIENT_FILL.center.x,
|
|
1743
|
+
y: Number.isFinite(params.gradientCenterY)
|
|
1744
|
+
? params.gradientCenterY
|
|
1745
|
+
: DEFAULT_RADIAL_GRADIENT_FILL.center.y,
|
|
1746
|
+
},
|
|
1747
|
+
radius: Number.isFinite(params.gradientRadius)
|
|
1748
|
+
? params.gradientRadius
|
|
1749
|
+
: DEFAULT_RADIAL_GRADIENT_FILL.radius,
|
|
1750
|
+
stops: explicitStops ?? [
|
|
1751
|
+
{
|
|
1752
|
+
color: params.gradientStartColor || DEFAULT_RADIAL_GRADIENT_FILL.stops[0].color,
|
|
1753
|
+
opacity: DEFAULT_RADIAL_GRADIENT_FILL.stops[0].opacity,
|
|
1754
|
+
position: DEFAULT_RADIAL_GRADIENT_FILL.stops[0].position,
|
|
1755
|
+
},
|
|
1756
|
+
{
|
|
1757
|
+
color: params.gradientEndColor || DEFAULT_RADIAL_GRADIENT_FILL.stops[1].color,
|
|
1758
|
+
opacity: DEFAULT_RADIAL_GRADIENT_FILL.stops[1].opacity,
|
|
1759
|
+
position: DEFAULT_RADIAL_GRADIENT_FILL.stops[1].position,
|
|
1760
|
+
},
|
|
1761
|
+
],
|
|
1762
|
+
})
|
|
1763
|
+
: normalizeLinearGradientFill({
|
|
1764
|
+
type: 'linear',
|
|
1765
|
+
angle: Number.isFinite(params.gradientAngle)
|
|
1766
|
+
? params.gradientAngle
|
|
1767
|
+
: DEFAULT_LINEAR_GRADIENT_FILL.angle,
|
|
1768
|
+
stops: explicitStops ?? [
|
|
1769
|
+
{
|
|
1770
|
+
color: params.gradientStartColor || DEFAULT_LINEAR_GRADIENT_FILL.stops[0].color,
|
|
1771
|
+
opacity: DEFAULT_LINEAR_GRADIENT_FILL.stops[0].opacity,
|
|
1772
|
+
position: DEFAULT_LINEAR_GRADIENT_FILL.stops[0].position,
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
color: params.gradientEndColor || DEFAULT_LINEAR_GRADIENT_FILL.stops[1].color,
|
|
1776
|
+
opacity: DEFAULT_LINEAR_GRADIENT_FILL.stops[1].opacity,
|
|
1777
|
+
position: DEFAULT_LINEAR_GRADIENT_FILL.stops[1].position,
|
|
1778
|
+
},
|
|
1779
|
+
],
|
|
1780
|
+
});
|
|
1904
1781
|
bgLayer.fill = gradient;
|
|
1905
1782
|
bgLayer.solidColor = gradient.stops[0].color;
|
|
1906
1783
|
currentPoster.canvas.backgroundColor = gradient.stops[0].color;
|
|
@@ -1945,7 +1822,6 @@ async function handleStateTool(name, args) {
|
|
|
1945
1822
|
objectFit: params.objectFit || 'cover',
|
|
1946
1823
|
};
|
|
1947
1824
|
}
|
|
1948
|
-
persistState();
|
|
1949
1825
|
return {
|
|
1950
1826
|
content: [
|
|
1951
1827
|
{
|
|
@@ -1960,7 +1836,11 @@ async function handleStateTool(name, args) {
|
|
|
1960
1836
|
};
|
|
1961
1837
|
}
|
|
1962
1838
|
case 'add_layer': {
|
|
1963
|
-
|
|
1839
|
+
if (!currentPoster) {
|
|
1840
|
+
return {
|
|
1841
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1964
1844
|
const layerType = params.type;
|
|
1965
1845
|
if (!layerType) {
|
|
1966
1846
|
return {
|
|
@@ -2080,17 +1960,14 @@ async function handleStateTool(name, args) {
|
|
|
2080
1960
|
}
|
|
2081
1961
|
else if (layerType === 'rectangle') {
|
|
2082
1962
|
const fillColor = normalizeHexColor(params.fillColor) || '#3b82f6';
|
|
2083
|
-
const fillOpacity = normalizeOpacity(params.fillOpacity, 1);
|
|
2084
1963
|
const strokeColor = normalizeHexColor(params.strokeColor) || '#000000';
|
|
2085
|
-
const gradientFill = buildFillFromParams(params);
|
|
2086
1964
|
layer = {
|
|
2087
1965
|
...baseLayer,
|
|
2088
1966
|
type: 'rectangle',
|
|
2089
1967
|
style: {
|
|
2090
|
-
fill: gradientFill ?? { type: 'solid', color: fillColor, opacity: fillOpacity },
|
|
2091
1968
|
fillType: 'solid',
|
|
2092
1969
|
fillColor,
|
|
2093
|
-
fillOpacity,
|
|
1970
|
+
fillOpacity: normalizeOpacity(params.fillOpacity, 1),
|
|
2094
1971
|
strokeColor,
|
|
2095
1972
|
strokeWidth: normalizeStrokeWidth(params.strokeWidth, 0),
|
|
2096
1973
|
strokeOpacity: normalizeOpacity(params.strokeOpacity, 1),
|
|
@@ -2105,19 +1982,16 @@ async function handleStateTool(name, args) {
|
|
|
2105
1982
|
}
|
|
2106
1983
|
else if (layerType === 'ellipse') {
|
|
2107
1984
|
const fillColor = normalizeHexColor(params.fillColor) || '#3b82f6';
|
|
2108
|
-
const fillOpacity = normalizeOpacity(params.fillOpacity, 1);
|
|
2109
1985
|
const strokeColor = normalizeHexColor(params.strokeColor) || '#000000';
|
|
2110
1986
|
const w = cssPercentToSize(params.width, 20);
|
|
2111
1987
|
const h = cssPercentToSize(params.height, 20);
|
|
2112
|
-
const gradientFill = buildFillFromParams(params);
|
|
2113
1988
|
layer = {
|
|
2114
1989
|
...baseLayer,
|
|
2115
1990
|
type: 'ellipse',
|
|
2116
1991
|
style: {
|
|
2117
|
-
fill: gradientFill ?? { type: 'solid', color: fillColor, opacity: fillOpacity },
|
|
2118
1992
|
fillType: 'solid',
|
|
2119
1993
|
fillColor,
|
|
2120
|
-
fillOpacity,
|
|
1994
|
+
fillOpacity: normalizeOpacity(params.fillOpacity, 1),
|
|
2121
1995
|
strokeColor,
|
|
2122
1996
|
strokeWidth: normalizeStrokeWidth(params.strokeWidth, 0),
|
|
2123
1997
|
strokeOpacity: normalizeOpacity(params.strokeOpacity, 1),
|
|
@@ -2130,19 +2004,16 @@ async function handleStateTool(name, args) {
|
|
|
2130
2004
|
}
|
|
2131
2005
|
else if (layerType === 'polygon') {
|
|
2132
2006
|
const fillColor = normalizeHexColor(params.fillColor) || '#3b82f6';
|
|
2133
|
-
const fillOpacity = normalizeOpacity(params.fillOpacity, 1);
|
|
2134
2007
|
const strokeColor = normalizeHexColor(params.strokeColor) || '#000000';
|
|
2135
2008
|
const w = cssPercentToSize(params.width, 20);
|
|
2136
2009
|
const h = cssPercentToSize(params.height, 20);
|
|
2137
|
-
const gradientFill = buildFillFromParams(params);
|
|
2138
2010
|
layer = {
|
|
2139
2011
|
...baseLayer,
|
|
2140
2012
|
type: 'polygon',
|
|
2141
2013
|
style: {
|
|
2142
|
-
fill: gradientFill ?? { type: 'solid', color: fillColor, opacity: fillOpacity },
|
|
2143
2014
|
fillType: 'solid',
|
|
2144
2015
|
fillColor,
|
|
2145
|
-
fillOpacity,
|
|
2016
|
+
fillOpacity: normalizeOpacity(params.fillOpacity, 1),
|
|
2146
2017
|
strokeColor,
|
|
2147
2018
|
strokeWidth: normalizeStrokeWidth(params.strokeWidth, 0),
|
|
2148
2019
|
strokeOpacity: normalizeOpacity(params.strokeOpacity, 1),
|
|
@@ -2156,19 +2027,16 @@ async function handleStateTool(name, args) {
|
|
|
2156
2027
|
}
|
|
2157
2028
|
else if (layerType === 'star') {
|
|
2158
2029
|
const fillColor = normalizeHexColor(params.fillColor) || '#3b82f6';
|
|
2159
|
-
const fillOpacity = normalizeOpacity(params.fillOpacity, 1);
|
|
2160
2030
|
const strokeColor = normalizeHexColor(params.strokeColor) || '#000000';
|
|
2161
2031
|
const w = cssPercentToSize(params.width, 20);
|
|
2162
2032
|
const h = cssPercentToSize(params.height, 20);
|
|
2163
|
-
const gradientFill = buildFillFromParams(params);
|
|
2164
2033
|
layer = {
|
|
2165
2034
|
...baseLayer,
|
|
2166
2035
|
type: 'star',
|
|
2167
2036
|
style: {
|
|
2168
|
-
fill: gradientFill ?? { type: 'solid', color: fillColor, opacity: fillOpacity },
|
|
2169
2037
|
fillType: 'solid',
|
|
2170
2038
|
fillColor,
|
|
2171
|
-
fillOpacity,
|
|
2039
|
+
fillOpacity: normalizeOpacity(params.fillOpacity, 1),
|
|
2172
2040
|
strokeColor,
|
|
2173
2041
|
strokeWidth: normalizeStrokeWidth(params.strokeWidth, 0),
|
|
2174
2042
|
strokeOpacity: normalizeOpacity(params.strokeOpacity, 1),
|
|
@@ -2211,7 +2079,6 @@ async function handleStateTool(name, args) {
|
|
|
2211
2079
|
layer = baseLayer;
|
|
2212
2080
|
}
|
|
2213
2081
|
currentPoster.layers.push(layer);
|
|
2214
|
-
persistState();
|
|
2215
2082
|
return {
|
|
2216
2083
|
content: [
|
|
2217
2084
|
{
|
|
@@ -2227,7 +2094,11 @@ async function handleStateTool(name, args) {
|
|
|
2227
2094
|
};
|
|
2228
2095
|
}
|
|
2229
2096
|
case 'add_group': {
|
|
2230
|
-
|
|
2097
|
+
if (!currentPoster) {
|
|
2098
|
+
return {
|
|
2099
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2231
2102
|
const children = params.children;
|
|
2232
2103
|
if (!children || !Array.isArray(children) || children.length === 0) {
|
|
2233
2104
|
return {
|
|
@@ -2333,17 +2204,14 @@ async function handleStateTool(name, args) {
|
|
|
2333
2204
|
}
|
|
2334
2205
|
else if (childType === 'rectangle') {
|
|
2335
2206
|
const fillColor = normalizeHexColor(childParams.fillColor) || '#3b82f6';
|
|
2336
|
-
const fillOpacity = normalizeOpacity(childParams.fillOpacity, 1);
|
|
2337
2207
|
const strokeColor = normalizeHexColor(childParams.strokeColor) || '#000000';
|
|
2338
|
-
const gradientFill = buildFillFromParams(childParams);
|
|
2339
2208
|
childLayers.push({
|
|
2340
2209
|
...childBase,
|
|
2341
2210
|
type: 'rectangle',
|
|
2342
2211
|
style: {
|
|
2343
|
-
fill: gradientFill ?? { type: 'solid', color: fillColor, opacity: fillOpacity },
|
|
2344
2212
|
fillType: 'solid',
|
|
2345
2213
|
fillColor,
|
|
2346
|
-
fillOpacity,
|
|
2214
|
+
fillOpacity: normalizeOpacity(childParams.fillOpacity, 1),
|
|
2347
2215
|
strokeColor,
|
|
2348
2216
|
strokeWidth: normalizeStrokeWidth(childParams.strokeWidth, 0),
|
|
2349
2217
|
strokeOpacity: normalizeOpacity(childParams.strokeOpacity, 1),
|
|
@@ -2358,19 +2226,16 @@ async function handleStateTool(name, args) {
|
|
|
2358
2226
|
}
|
|
2359
2227
|
else if (childType === 'ellipse') {
|
|
2360
2228
|
const fillColor = normalizeHexColor(childParams.fillColor) || '#3b82f6';
|
|
2361
|
-
const fillOpacity = normalizeOpacity(childParams.fillOpacity, 1);
|
|
2362
2229
|
const strokeColor = normalizeHexColor(childParams.strokeColor) || '#000000';
|
|
2363
2230
|
const w = cssPercentToSize(childParams.width, 20);
|
|
2364
2231
|
const h = cssPercentToSize(childParams.height, 20);
|
|
2365
|
-
const gradientFill = buildFillFromParams(childParams);
|
|
2366
2232
|
childLayers.push({
|
|
2367
2233
|
...childBase,
|
|
2368
2234
|
type: 'ellipse',
|
|
2369
2235
|
style: {
|
|
2370
|
-
fill: gradientFill ?? { type: 'solid', color: fillColor, opacity: fillOpacity },
|
|
2371
2236
|
fillType: 'solid',
|
|
2372
2237
|
fillColor,
|
|
2373
|
-
fillOpacity,
|
|
2238
|
+
fillOpacity: normalizeOpacity(childParams.fillOpacity, 1),
|
|
2374
2239
|
strokeColor,
|
|
2375
2240
|
strokeWidth: normalizeStrokeWidth(childParams.strokeWidth, 0),
|
|
2376
2241
|
strokeOpacity: normalizeOpacity(childParams.strokeOpacity, 1),
|
|
@@ -2383,19 +2248,16 @@ async function handleStateTool(name, args) {
|
|
|
2383
2248
|
}
|
|
2384
2249
|
else if (childType === 'polygon') {
|
|
2385
2250
|
const fillColor = normalizeHexColor(childParams.fillColor) || '#3b82f6';
|
|
2386
|
-
const fillOpacity = normalizeOpacity(childParams.fillOpacity, 1);
|
|
2387
2251
|
const strokeColor = normalizeHexColor(childParams.strokeColor) || '#000000';
|
|
2388
2252
|
const w = cssPercentToSize(childParams.width, 20);
|
|
2389
2253
|
const h = cssPercentToSize(childParams.height, 20);
|
|
2390
|
-
const gradientFill = buildFillFromParams(childParams);
|
|
2391
2254
|
childLayers.push({
|
|
2392
2255
|
...childBase,
|
|
2393
2256
|
type: 'polygon',
|
|
2394
2257
|
style: {
|
|
2395
|
-
fill: gradientFill ?? { type: 'solid', color: fillColor, opacity: fillOpacity },
|
|
2396
2258
|
fillType: 'solid',
|
|
2397
2259
|
fillColor,
|
|
2398
|
-
fillOpacity,
|
|
2260
|
+
fillOpacity: normalizeOpacity(childParams.fillOpacity, 1),
|
|
2399
2261
|
strokeColor,
|
|
2400
2262
|
strokeWidth: normalizeStrokeWidth(childParams.strokeWidth, 0),
|
|
2401
2263
|
strokeOpacity: normalizeOpacity(childParams.strokeOpacity, 1),
|
|
@@ -2409,19 +2271,16 @@ async function handleStateTool(name, args) {
|
|
|
2409
2271
|
}
|
|
2410
2272
|
else if (childType === 'star') {
|
|
2411
2273
|
const fillColor = normalizeHexColor(childParams.fillColor) || '#3b82f6';
|
|
2412
|
-
const fillOpacity = normalizeOpacity(childParams.fillOpacity, 1);
|
|
2413
2274
|
const strokeColor = normalizeHexColor(childParams.strokeColor) || '#000000';
|
|
2414
2275
|
const w = cssPercentToSize(childParams.width, 20);
|
|
2415
2276
|
const h = cssPercentToSize(childParams.height, 20);
|
|
2416
|
-
const gradientFill = buildFillFromParams(childParams);
|
|
2417
2277
|
childLayers.push({
|
|
2418
2278
|
...childBase,
|
|
2419
2279
|
type: 'star',
|
|
2420
2280
|
style: {
|
|
2421
|
-
fill: gradientFill ?? { type: 'solid', color: fillColor, opacity: fillOpacity },
|
|
2422
2281
|
fillType: 'solid',
|
|
2423
2282
|
fillColor,
|
|
2424
|
-
fillOpacity,
|
|
2283
|
+
fillOpacity: normalizeOpacity(childParams.fillOpacity, 1),
|
|
2425
2284
|
strokeColor,
|
|
2426
2285
|
strokeWidth: normalizeStrokeWidth(childParams.strokeWidth, 0),
|
|
2427
2286
|
strokeOpacity: normalizeOpacity(childParams.strokeOpacity, 1),
|
|
@@ -2511,7 +2370,6 @@ async function handleStateTool(name, args) {
|
|
|
2511
2370
|
...(params.cornerRadius !== undefined ? { cornerRadius: params.cornerRadius } : {}),
|
|
2512
2371
|
};
|
|
2513
2372
|
currentPoster.layers.push(groupLayer);
|
|
2514
|
-
persistState();
|
|
2515
2373
|
return {
|
|
2516
2374
|
content: [
|
|
2517
2375
|
{
|
|
@@ -2527,7 +2385,11 @@ async function handleStateTool(name, args) {
|
|
|
2527
2385
|
};
|
|
2528
2386
|
}
|
|
2529
2387
|
case 'modify_layer': {
|
|
2530
|
-
|
|
2388
|
+
if (!currentPoster) {
|
|
2389
|
+
return {
|
|
2390
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
2391
|
+
};
|
|
2392
|
+
}
|
|
2531
2393
|
const layerId = params.layerId;
|
|
2532
2394
|
if (!layerId) {
|
|
2533
2395
|
return {
|
|
@@ -2875,19 +2737,6 @@ async function handleStateTool(name, args) {
|
|
|
2875
2737
|
const existing = asFiniteNumber(anyLayer.style.strokeOpacity, 1);
|
|
2876
2738
|
anyLayer.style.strokeOpacity = normalizeOpacity(params.strokeOpacity, existing);
|
|
2877
2739
|
}
|
|
2878
|
-
// Gradient fill update
|
|
2879
|
-
const modifyGradientFill = buildFillFromParams(params);
|
|
2880
|
-
if (modifyGradientFill) {
|
|
2881
|
-
anyLayer.style.fill = modifyGradientFill;
|
|
2882
|
-
anyLayer.style.fillType = 'solid';
|
|
2883
|
-
}
|
|
2884
|
-
else if (params.fillColor !== undefined || params.fillOpacity !== undefined) {
|
|
2885
|
-
// Sync style.fill as solid when only color/opacity changed
|
|
2886
|
-
const currentFillColor = typeof anyLayer.style.fillColor === 'string' ? anyLayer.style.fillColor : '#3b82f6';
|
|
2887
|
-
const currentFillOpacity = asFiniteNumber(anyLayer.style.fillOpacity, 1);
|
|
2888
|
-
anyLayer.style.fill = { type: 'solid', color: currentFillColor, opacity: currentFillOpacity };
|
|
2889
|
-
anyLayer.style.fillType = 'solid';
|
|
2890
|
-
}
|
|
2891
2740
|
if (anyLayer.style.fillType === undefined && layer.type !== 'line')
|
|
2892
2741
|
anyLayer.style.fillType = 'solid';
|
|
2893
2742
|
if (anyLayer.style.strokeCap === undefined)
|
|
@@ -2967,7 +2816,6 @@ async function handleStateTool(name, args) {
|
|
|
2967
2816
|
layer.transform.height = 1;
|
|
2968
2817
|
}
|
|
2969
2818
|
}
|
|
2970
|
-
persistState();
|
|
2971
2819
|
return {
|
|
2972
2820
|
content: [
|
|
2973
2821
|
{
|
|
@@ -2982,7 +2830,11 @@ async function handleStateTool(name, args) {
|
|
|
2982
2830
|
};
|
|
2983
2831
|
}
|
|
2984
2832
|
case 'apply_effect': {
|
|
2985
|
-
|
|
2833
|
+
if (!currentPoster) {
|
|
2834
|
+
return {
|
|
2835
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2986
2838
|
const effectId = params.effectId;
|
|
2987
2839
|
if (!effectId) {
|
|
2988
2840
|
return {
|
|
@@ -2991,7 +2843,6 @@ async function handleStateTool(name, args) {
|
|
|
2991
2843
|
}
|
|
2992
2844
|
if (effectId === 'none') {
|
|
2993
2845
|
delete currentPoster.effect;
|
|
2994
|
-
persistState();
|
|
2995
2846
|
return {
|
|
2996
2847
|
content: [
|
|
2997
2848
|
{
|
|
@@ -3239,7 +3090,6 @@ async function handleStateTool(name, args) {
|
|
|
3239
3090
|
};
|
|
3240
3091
|
}
|
|
3241
3092
|
currentPoster.effect = effect;
|
|
3242
|
-
persistState();
|
|
3243
3093
|
return {
|
|
3244
3094
|
content: [
|
|
3245
3095
|
{
|
|
@@ -3254,7 +3104,11 @@ async function handleStateTool(name, args) {
|
|
|
3254
3104
|
};
|
|
3255
3105
|
}
|
|
3256
3106
|
case 'add_postprocess': {
|
|
3257
|
-
|
|
3107
|
+
if (!currentPoster) {
|
|
3108
|
+
return {
|
|
3109
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
3110
|
+
};
|
|
3111
|
+
}
|
|
3258
3112
|
const ppType = params.type;
|
|
3259
3113
|
if (!ppType) {
|
|
3260
3114
|
return {
|
|
@@ -3336,7 +3190,6 @@ async function handleStateTool(name, args) {
|
|
|
3336
3190
|
settings,
|
|
3337
3191
|
};
|
|
3338
3192
|
currentPoster.postProcesses.push(postProcess);
|
|
3339
|
-
persistState();
|
|
3340
3193
|
// Build response with warning if dither is active
|
|
3341
3194
|
const response = {
|
|
3342
3195
|
success: true,
|
|
@@ -3359,7 +3212,11 @@ async function handleStateTool(name, args) {
|
|
|
3359
3212
|
};
|
|
3360
3213
|
}
|
|
3361
3214
|
case 'remove_layer': {
|
|
3362
|
-
|
|
3215
|
+
if (!currentPoster) {
|
|
3216
|
+
return {
|
|
3217
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3363
3220
|
const layerId = params.layerId;
|
|
3364
3221
|
if (!layerId) {
|
|
3365
3222
|
return {
|
|
@@ -3378,7 +3235,6 @@ async function handleStateTool(name, args) {
|
|
|
3378
3235
|
};
|
|
3379
3236
|
}
|
|
3380
3237
|
currentPoster.layers.splice(layerIndex, 1);
|
|
3381
|
-
persistState();
|
|
3382
3238
|
return {
|
|
3383
3239
|
content: [
|
|
3384
3240
|
{
|
|
@@ -3393,7 +3249,11 @@ async function handleStateTool(name, args) {
|
|
|
3393
3249
|
};
|
|
3394
3250
|
}
|
|
3395
3251
|
case 'move_layer': {
|
|
3396
|
-
|
|
3252
|
+
if (!currentPoster) {
|
|
3253
|
+
return {
|
|
3254
|
+
content: [{ type: 'text', text: 'Error: No poster created. Use create_poster first.' }],
|
|
3255
|
+
};
|
|
3256
|
+
}
|
|
3397
3257
|
const poster = currentPoster;
|
|
3398
3258
|
const layerId = params.layerId;
|
|
3399
3259
|
const direction = params.direction;
|
|
@@ -3436,7 +3296,6 @@ async function handleStateTool(name, args) {
|
|
|
3436
3296
|
content: [{ type: 'text', text: 'Error: direction must be one of: up, down, top, bottom' }],
|
|
3437
3297
|
};
|
|
3438
3298
|
}
|
|
3439
|
-
persistState();
|
|
3440
3299
|
return {
|
|
3441
3300
|
content: [
|
|
3442
3301
|
{
|
|
@@ -3452,7 +3311,11 @@ async function handleStateTool(name, args) {
|
|
|
3452
3311
|
};
|
|
3453
3312
|
}
|
|
3454
3313
|
case 'get_state': {
|
|
3455
|
-
|
|
3314
|
+
if (!currentPoster) {
|
|
3315
|
+
return {
|
|
3316
|
+
content: [{ type: 'text', text: 'No poster created yet. Use create_poster first.' }],
|
|
3317
|
+
};
|
|
3318
|
+
}
|
|
3456
3319
|
return {
|
|
3457
3320
|
content: [
|
|
3458
3321
|
{
|