figma-console-mcp 1.7.0 → 1.9.0
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 +81 -52
- package/dist/cloudflare/core/comment-tools.js +292 -0
- package/dist/cloudflare/core/design-code-tools.js +2 -2
- package/dist/cloudflare/core/figma-api.js +34 -0
- package/dist/cloudflare/core/figma-connector.js +8 -0
- package/dist/cloudflare/core/figma-desktop-connector.js +69 -2
- package/dist/cloudflare/core/figma-tools.js +118 -98
- package/dist/cloudflare/core/websocket-connector.js +235 -0
- package/dist/cloudflare/core/websocket-server.js +603 -0
- package/dist/cloudflare/index.js +9 -6
- package/dist/core/comment-tools.d.ts +11 -0
- package/dist/core/comment-tools.d.ts.map +1 -0
- package/dist/core/comment-tools.js +293 -0
- package/dist/core/comment-tools.js.map +1 -0
- package/dist/core/design-code-tools.js +2 -2
- package/dist/core/design-code-tools.js.map +1 -1
- package/dist/core/figma-api.d.ts +23 -0
- package/dist/core/figma-api.d.ts.map +1 -1
- package/dist/core/figma-api.js +34 -0
- package/dist/core/figma-api.js.map +1 -1
- package/dist/core/figma-connector.d.ts +47 -0
- package/dist/core/figma-connector.d.ts.map +1 -0
- package/dist/core/figma-connector.js +9 -0
- package/dist/core/figma-connector.js.map +1 -0
- package/dist/core/figma-desktop-connector.d.ts +14 -1
- package/dist/core/figma-desktop-connector.d.ts.map +1 -1
- package/dist/core/figma-desktop-connector.js +69 -2
- package/dist/core/figma-desktop-connector.js.map +1 -1
- package/dist/core/figma-tools.d.ts +1 -1
- package/dist/core/figma-tools.d.ts.map +1 -1
- package/dist/core/figma-tools.js +118 -98
- package/dist/core/figma-tools.js.map +1 -1
- package/dist/core/websocket-connector.d.ts +53 -0
- package/dist/core/websocket-connector.d.ts.map +1 -0
- package/dist/core/websocket-connector.js +236 -0
- package/dist/core/websocket-connector.js.map +1 -0
- package/dist/core/websocket-server.d.ts +183 -0
- package/dist/core/websocket-server.d.ts.map +1 -0
- package/dist/core/websocket-server.js +604 -0
- package/dist/core/websocket-server.js.map +1 -0
- package/dist/local.d.ts +11 -3
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +847 -276
- package/dist/local.js.map +1 -1
- package/figma-desktop-bridge/README.md +30 -16
- package/figma-desktop-bridge/code.js +195 -3
- package/figma-desktop-bridge/manifest.json +4 -1
- package/figma-desktop-bridge/ui.html +229 -0
- package/package.json +6 -4
|
@@ -45,6 +45,9 @@ export class FigmaDesktopConnector {
|
|
|
45
45
|
async initialize() {
|
|
46
46
|
logger.info('Figma Desktop connector initialized (using Puppeteer Worker API)');
|
|
47
47
|
}
|
|
48
|
+
getTransportType() {
|
|
49
|
+
return 'cdp';
|
|
50
|
+
}
|
|
48
51
|
/**
|
|
49
52
|
* Execute code in Figma's plugin context where the figma API is available
|
|
50
53
|
* Uses Puppeteer's direct worker access instead of CDP context enumeration
|
|
@@ -707,7 +710,18 @@ export class FigmaDesktopConnector {
|
|
|
707
710
|
logger.info({ variableId, newName }, 'Renaming variable via plugin UI');
|
|
708
711
|
const frame = await this.findPluginUIFrame();
|
|
709
712
|
try {
|
|
710
|
-
|
|
713
|
+
// Look up old name from cached variables data, then rename — single CDP roundtrip
|
|
714
|
+
const result = await frame.evaluate(`(async () => {
|
|
715
|
+
var oldName;
|
|
716
|
+
var vars = window.__figmaVariablesData;
|
|
717
|
+
if (vars && vars.variables) {
|
|
718
|
+
var v = vars.variables.find(function(v) { return v.id === ${JSON.stringify(variableId)}; });
|
|
719
|
+
if (v) oldName = v.name;
|
|
720
|
+
}
|
|
721
|
+
var result = await window.renameVariable(${JSON.stringify(variableId)}, ${JSON.stringify(newName)});
|
|
722
|
+
if (oldName) result.oldName = oldName;
|
|
723
|
+
return result;
|
|
724
|
+
})()`);
|
|
711
725
|
logger.info({ success: result.success, oldName: result.oldName, newName: result.variable?.name }, 'Variable renamed');
|
|
712
726
|
await this.logToFigmaConsole((oldN, newN) => {
|
|
713
727
|
console.log(`[DESKTOP_CONNECTOR] ✅ Variable renamed from "${oldN}" to "${newN}"`);
|
|
@@ -773,7 +787,25 @@ export class FigmaDesktopConnector {
|
|
|
773
787
|
logger.info({ collectionId, modeId, newName }, 'Renaming mode via plugin UI');
|
|
774
788
|
const frame = await this.findPluginUIFrame();
|
|
775
789
|
try {
|
|
776
|
-
|
|
790
|
+
// Look up old mode name from cached variables data, then rename — single CDP roundtrip
|
|
791
|
+
const result = await frame.evaluate(`(async () => {
|
|
792
|
+
var oldName;
|
|
793
|
+
var vars = window.__figmaVariablesData;
|
|
794
|
+
var colls = vars && (vars.variableCollections || vars.collections);
|
|
795
|
+
if (colls) {
|
|
796
|
+
for (var i = 0; i < colls.length; i++) {
|
|
797
|
+
var c = colls[i];
|
|
798
|
+
if (c.id === ${JSON.stringify(collectionId)}) {
|
|
799
|
+
var mode = c.modes.find(function(m) { return m.modeId === ${JSON.stringify(modeId)}; });
|
|
800
|
+
if (mode) oldName = mode.name;
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
var result = await window.renameMode(${JSON.stringify(collectionId)}, ${JSON.stringify(modeId)}, ${JSON.stringify(newName)});
|
|
806
|
+
if (oldName) result.oldName = oldName;
|
|
807
|
+
return result;
|
|
808
|
+
})()`);
|
|
777
809
|
logger.info({ success: result.success, oldName: result.oldName, newName }, 'Mode renamed');
|
|
778
810
|
await this.logToFigmaConsole((oldN, newN) => {
|
|
779
811
|
console.log(`[DESKTOP_CONNECTOR] ✅ Mode renamed from "${oldN}" to "${newN}"`);
|
|
@@ -1080,5 +1112,40 @@ export class FigmaDesktopConnector {
|
|
|
1080
1112
|
throw error;
|
|
1081
1113
|
}
|
|
1082
1114
|
}
|
|
1115
|
+
// ============================================================================
|
|
1116
|
+
// SCREENSHOT & INSTANCE PROPERTIES (via plugin UI)
|
|
1117
|
+
// ============================================================================
|
|
1118
|
+
/**
|
|
1119
|
+
* Capture screenshot via plugin's exportAsync
|
|
1120
|
+
*/
|
|
1121
|
+
async captureScreenshot(nodeId, options) {
|
|
1122
|
+
logger.info({ nodeId, options }, 'Capturing screenshot via plugin UI');
|
|
1123
|
+
const frame = await this.findPluginUIFrame();
|
|
1124
|
+
try {
|
|
1125
|
+
const result = await frame.evaluate(`window.captureScreenshot(${JSON.stringify(nodeId)}, ${JSON.stringify(options || {})})`);
|
|
1126
|
+
logger.info({ success: result.success }, 'Screenshot captured');
|
|
1127
|
+
return result;
|
|
1128
|
+
}
|
|
1129
|
+
catch (error) {
|
|
1130
|
+
logger.error({ error, nodeId }, 'Capture screenshot failed');
|
|
1131
|
+
throw error;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Set component instance properties
|
|
1136
|
+
*/
|
|
1137
|
+
async setInstanceProperties(nodeId, properties) {
|
|
1138
|
+
logger.info({ nodeId, properties: Object.keys(properties || {}) }, 'Setting instance properties via plugin UI');
|
|
1139
|
+
const frame = await this.findPluginUIFrame();
|
|
1140
|
+
try {
|
|
1141
|
+
const result = await frame.evaluate(`window.setInstanceProperties(${JSON.stringify(nodeId)}, ${JSON.stringify(properties)})`);
|
|
1142
|
+
logger.info({ success: result.success }, 'Instance properties set');
|
|
1143
|
+
return result;
|
|
1144
|
+
}
|
|
1145
|
+
catch (error) {
|
|
1146
|
+
logger.error({ error, nodeId }, 'Set instance properties failed');
|
|
1147
|
+
throw error;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1083
1150
|
}
|
|
1084
1151
|
FigmaDesktopConnector.DEBUG = process.env.DEBUG === '1' || process.env.DEBUG === 'true';
|
|
@@ -574,7 +574,7 @@ function resolveVariableAliases(variables, allVariablesMap, collectionsMap) {
|
|
|
574
574
|
/**
|
|
575
575
|
* Register Figma API tools with the MCP server
|
|
576
576
|
*/
|
|
577
|
-
export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getConsoleMonitor, getBrowserManager, ensureInitialized, variablesCache, options) {
|
|
577
|
+
export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getConsoleMonitor, getBrowserManager, ensureInitialized, variablesCache, options, getDesktopConnector) {
|
|
578
578
|
const isRemoteMode = options?.isRemoteMode ?? false;
|
|
579
579
|
// Tool 8: Get File Data (General Purpose)
|
|
580
580
|
// NOTE: For specific use cases, consider using specialized tools:
|
|
@@ -585,7 +585,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
585
585
|
.string()
|
|
586
586
|
.url()
|
|
587
587
|
.optional()
|
|
588
|
-
.describe("Figma file URL (e.g., https://figma.com/design/abc123).
|
|
588
|
+
.describe("Figma file URL (e.g., https://figma.com/design/abc123). Auto-detected from CDP browser or WebSocket Desktop Bridge connection. Only required if neither is connected."),
|
|
589
589
|
depth: z
|
|
590
590
|
.number()
|
|
591
591
|
.min(0)
|
|
@@ -626,7 +626,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
626
626
|
// Use provided URL or current URL from browser
|
|
627
627
|
const url = fileUrl || getCurrentUrl();
|
|
628
628
|
if (!url) {
|
|
629
|
-
throw new Error("No Figma file URL
|
|
629
|
+
throw new Error("No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).");
|
|
630
630
|
}
|
|
631
631
|
const fileKey = extractFileKey(url);
|
|
632
632
|
if (!fileKey) {
|
|
@@ -786,7 +786,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
786
786
|
.string()
|
|
787
787
|
.url()
|
|
788
788
|
.optional()
|
|
789
|
-
.describe("Figma file URL (e.g., https://figma.com/design/abc123).
|
|
789
|
+
.describe("Figma file URL (e.g., https://figma.com/design/abc123). Auto-detected from CDP browser or WebSocket Desktop Bridge connection. Only required if neither is connected."),
|
|
790
790
|
includePublished: z
|
|
791
791
|
.boolean()
|
|
792
792
|
.optional()
|
|
@@ -895,8 +895,8 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
895
895
|
{
|
|
896
896
|
type: "text",
|
|
897
897
|
text: JSON.stringify({
|
|
898
|
-
error: "No Figma file URL
|
|
899
|
-
message: "
|
|
898
|
+
error: "No Figma file URL available",
|
|
899
|
+
message: "Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode)."
|
|
900
900
|
}),
|
|
901
901
|
},
|
|
902
902
|
],
|
|
@@ -1447,16 +1447,17 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
1447
1447
|
}
|
|
1448
1448
|
}
|
|
1449
1449
|
// FALLBACK: Try Desktop connection (when no token available OR as secondary fallback)
|
|
1450
|
-
//
|
|
1451
|
-
if (ensureInitialized && !parseFromConsole && (!hasToken || !restApiSucceeded)) {
|
|
1452
|
-
logger.info("Calling ensureInitialized to initialize browser manager");
|
|
1450
|
+
// Only call ensureInitialized for CDP path — skip when transport-agnostic connector exists
|
|
1451
|
+
if (ensureInitialized && !getDesktopConnector && !parseFromConsole && (!hasToken || !restApiSucceeded)) {
|
|
1452
|
+
logger.info("Calling ensureInitialized to initialize browser manager (CDP path)");
|
|
1453
1453
|
await ensureInitialized();
|
|
1454
1454
|
}
|
|
1455
1455
|
const browserManager = getBrowserManager?.();
|
|
1456
|
-
|
|
1456
|
+
const hasDesktopConnection = !!getDesktopConnector || !!browserManager;
|
|
1457
|
+
logger.info({ hasBrowserManager: !!browserManager, hasDesktopConnector: !!getDesktopConnector, parseFromConsole, hasToken, restApiSucceeded }, "Desktop connection check");
|
|
1457
1458
|
// Debug: Log why Desktop connection might be skipped
|
|
1458
|
-
if (!
|
|
1459
|
-
logger.error("Desktop connection skipped:
|
|
1459
|
+
if (!hasDesktopConnection) {
|
|
1460
|
+
logger.error("Desktop connection skipped: neither connector nor browserManager available");
|
|
1460
1461
|
}
|
|
1461
1462
|
else if (parseFromConsole) {
|
|
1462
1463
|
logger.info("Desktop connection skipped: parseFromConsole is true");
|
|
@@ -1464,26 +1465,21 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
1464
1465
|
else if (restApiSucceeded) {
|
|
1465
1466
|
logger.info("Desktop connection skipped: REST API already succeeded");
|
|
1466
1467
|
}
|
|
1467
|
-
if (
|
|
1468
|
+
if (hasDesktopConnection && !parseFromConsole && (!hasToken || !restApiSucceeded)) {
|
|
1468
1469
|
try {
|
|
1469
1470
|
logger.info({ fileKey }, "Attempting to get variables via Desktop connection");
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
});
|
|
1482
|
-
await connector.initialize();
|
|
1483
|
-
logger.info("Desktop connector initialized, calling getVariablesFromPluginUI...");
|
|
1484
|
-
await page.evaluate(() => {
|
|
1485
|
-
console.log('[FIGMA_TOOLS] ✅ Desktop connector initialized, calling getVariablesFromPluginUI...');
|
|
1486
|
-
});
|
|
1471
|
+
let connector;
|
|
1472
|
+
if (getDesktopConnector) {
|
|
1473
|
+
connector = await getDesktopConnector();
|
|
1474
|
+
}
|
|
1475
|
+
else {
|
|
1476
|
+
// Fallback: direct CDP connector (legacy path)
|
|
1477
|
+
const { FigmaDesktopConnector } = await import('./figma-desktop-connector.js');
|
|
1478
|
+
const page = await browserManager.getPage();
|
|
1479
|
+
connector = new FigmaDesktopConnector(page);
|
|
1480
|
+
await connector.initialize();
|
|
1481
|
+
}
|
|
1482
|
+
logger.info({ transport: connector.getTransportType?.() || 'unknown' }, "Desktop connector ready");
|
|
1487
1483
|
const desktopResult = await connector.getVariablesFromPluginUI(fileKey);
|
|
1488
1484
|
if (desktopResult.success && desktopResult.variables) {
|
|
1489
1485
|
logger.info({
|
|
@@ -1841,7 +1837,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
1841
1837
|
.string()
|
|
1842
1838
|
.url()
|
|
1843
1839
|
.optional()
|
|
1844
|
-
.describe("Figma file URL (e.g., https://figma.com/design/abc123).
|
|
1840
|
+
.describe("Figma file URL (e.g., https://figma.com/design/abc123). Auto-detected from CDP browser or WebSocket Desktop Bridge connection. Only required if neither is connected."),
|
|
1845
1841
|
nodeId: z
|
|
1846
1842
|
.string()
|
|
1847
1843
|
.describe("Component node ID (e.g., '123:456')"),
|
|
@@ -1858,7 +1854,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
1858
1854
|
try {
|
|
1859
1855
|
const url = fileUrl || getCurrentUrl();
|
|
1860
1856
|
if (!url) {
|
|
1861
|
-
throw new Error("No Figma file URL
|
|
1857
|
+
throw new Error("No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).");
|
|
1862
1858
|
}
|
|
1863
1859
|
const fileKey = extractFileKey(url);
|
|
1864
1860
|
if (!fileKey) {
|
|
@@ -1866,18 +1862,26 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
1866
1862
|
}
|
|
1867
1863
|
logger.info({ fileKey, nodeId, format, enrich }, "Fetching component data");
|
|
1868
1864
|
// PRIORITY 1: Try Desktop Bridge plugin UI first (has reliable description field!)
|
|
1869
|
-
if (getBrowserManager && ensureInitialized) {
|
|
1865
|
+
if (getDesktopConnector || (getBrowserManager && ensureInitialized)) {
|
|
1870
1866
|
try {
|
|
1871
1867
|
logger.info({ nodeId }, "Attempting to get component via Desktop Bridge plugin UI");
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1868
|
+
let connector;
|
|
1869
|
+
if (getDesktopConnector) {
|
|
1870
|
+
connector = await getDesktopConnector();
|
|
1871
|
+
}
|
|
1872
|
+
else {
|
|
1873
|
+
// Fallback: direct CDP connector (legacy path)
|
|
1874
|
+
if (ensureInitialized)
|
|
1875
|
+
await ensureInitialized();
|
|
1876
|
+
const browserManager = getBrowserManager?.();
|
|
1877
|
+
if (!browserManager) {
|
|
1878
|
+
throw new Error("Browser manager not available after initialization");
|
|
1879
|
+
}
|
|
1880
|
+
const { FigmaDesktopConnector } = await import('./figma-desktop-connector.js');
|
|
1881
|
+
const page = await browserManager.getPage();
|
|
1882
|
+
connector = new FigmaDesktopConnector(page);
|
|
1883
|
+
await connector.initialize();
|
|
1876
1884
|
}
|
|
1877
|
-
const { FigmaDesktopConnector } = await import('./figma-desktop-connector.js');
|
|
1878
|
-
const page = await browserManager.getPage();
|
|
1879
|
-
const connector = new FigmaDesktopConnector(page);
|
|
1880
|
-
await connector.initialize();
|
|
1881
1885
|
const desktopResult = await connector.getComponentFromPluginUI(nodeId);
|
|
1882
1886
|
if (desktopResult.success && desktopResult.component) {
|
|
1883
1887
|
logger.info({
|
|
@@ -1969,7 +1973,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
1969
1973
|
catch (apiError) {
|
|
1970
1974
|
const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
|
|
1971
1975
|
throw new Error(`Cannot retrieve component data. Both Desktop Bridge and REST API are unavailable.\n` +
|
|
1972
|
-
`Desktop Bridge: ${getBrowserManager && ensureInitialized ? 'Failed (see logs above)' : 'Not available (local mode only)'}\n` +
|
|
1976
|
+
`Desktop Bridge: ${getDesktopConnector || (getBrowserManager && ensureInitialized) ? 'Failed (see logs above)' : 'Not available (local mode only)'}\n` +
|
|
1973
1977
|
`REST API: ${errorMessage}\n\n` +
|
|
1974
1978
|
`To fix:\n` +
|
|
1975
1979
|
`1. Local mode: Set FIGMA_ACCESS_TOKEN environment variable, OR ensure Figma Desktop Bridge plugin is running\n` +
|
|
@@ -2073,7 +2077,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2073
2077
|
.string()
|
|
2074
2078
|
.url()
|
|
2075
2079
|
.optional()
|
|
2076
|
-
.describe("Figma file URL (e.g., https://figma.com/design/abc123).
|
|
2080
|
+
.describe("Figma file URL (e.g., https://figma.com/design/abc123). Auto-detected from CDP browser or WebSocket Desktop Bridge connection. Only required if neither is connected."),
|
|
2077
2081
|
verbosity: z
|
|
2078
2082
|
.enum(["summary", "standard", "full"])
|
|
2079
2083
|
.optional()
|
|
@@ -2111,7 +2115,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2111
2115
|
}
|
|
2112
2116
|
const url = fileUrl || getCurrentUrl();
|
|
2113
2117
|
if (!url) {
|
|
2114
|
-
throw new Error("No Figma file URL
|
|
2118
|
+
throw new Error("No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).");
|
|
2115
2119
|
}
|
|
2116
2120
|
const fileKey = extractFileKey(url);
|
|
2117
2121
|
if (!fileKey) {
|
|
@@ -2218,7 +2222,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2218
2222
|
.string()
|
|
2219
2223
|
.url()
|
|
2220
2224
|
.optional()
|
|
2221
|
-
.describe("Figma file URL (e.g., https://figma.com/design/abc123).
|
|
2225
|
+
.describe("Figma file URL (e.g., https://figma.com/design/abc123). Auto-detected from CDP browser or WebSocket Desktop Bridge connection. Only required if neither is connected."),
|
|
2222
2226
|
nodeId: z
|
|
2223
2227
|
.string()
|
|
2224
2228
|
.describe("Component node ID to render as image (e.g., '695:313')"),
|
|
@@ -2252,7 +2256,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2252
2256
|
}
|
|
2253
2257
|
const url = fileUrl || getCurrentUrl();
|
|
2254
2258
|
if (!url) {
|
|
2255
|
-
throw new Error("No Figma file URL
|
|
2259
|
+
throw new Error("No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).");
|
|
2256
2260
|
}
|
|
2257
2261
|
const fileKey = extractFileKey(url);
|
|
2258
2262
|
if (!fileKey) {
|
|
@@ -2260,8 +2264,8 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2260
2264
|
}
|
|
2261
2265
|
logger.info({ fileKey, nodeId, scale, format }, "Rendering component image");
|
|
2262
2266
|
// First, fetch the node to check if it's a COMPONENT_SET
|
|
2263
|
-
const fileData = await api.
|
|
2264
|
-
const node = fileData.nodes[nodeId]?.document;
|
|
2267
|
+
const fileData = await api.getNodes(fileKey, [nodeId]);
|
|
2268
|
+
const node = fileData.nodes?.[nodeId]?.document;
|
|
2265
2269
|
if (!node) {
|
|
2266
2270
|
throw new Error(`Node ${nodeId} not found in file ${fileKey}. Please verify the node ID is correct.`);
|
|
2267
2271
|
}
|
|
@@ -2367,7 +2371,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2367
2371
|
}
|
|
2368
2372
|
const url = fileUrl || getCurrentUrl();
|
|
2369
2373
|
if (!url) {
|
|
2370
|
-
throw new Error("No Figma file URL
|
|
2374
|
+
throw new Error("No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).");
|
|
2371
2375
|
}
|
|
2372
2376
|
const fileKey = extractFileKey(url);
|
|
2373
2377
|
if (!fileKey) {
|
|
@@ -2577,7 +2581,7 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2577
2581
|
}
|
|
2578
2582
|
const url = fileUrl || getCurrentUrl();
|
|
2579
2583
|
if (!url) {
|
|
2580
|
-
throw new Error("No Figma file URL
|
|
2584
|
+
throw new Error("No Figma file URL available. Pass the fileUrl parameter, call figma_navigate (CDP mode), or ensure the Desktop Bridge plugin is connected (WebSocket mode).");
|
|
2581
2585
|
}
|
|
2582
2586
|
const fileKey = extractFileKey(url);
|
|
2583
2587
|
if (!fileKey) {
|
|
@@ -2752,33 +2756,43 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2752
2756
|
.describe("Scale factor (default: 2 for 2x resolution)"),
|
|
2753
2757
|
}, async ({ nodeId, format, scale }) => {
|
|
2754
2758
|
try {
|
|
2755
|
-
// Check for Desktop Bridge connection
|
|
2756
|
-
const browserManager = getBrowserManager?.();
|
|
2757
|
-
if (!browserManager) {
|
|
2758
|
-
throw new Error("Desktop Bridge not available. To capture screenshots:\n" +
|
|
2759
|
-
"1. Open your Figma file in Figma Desktop\n" +
|
|
2760
|
-
"2. Install and run the 'Figma Console MCP' plugin\n" +
|
|
2761
|
-
"3. Ensure the plugin shows 'MCP ready' status");
|
|
2762
|
-
}
|
|
2763
|
-
// Ensure browser is initialized
|
|
2764
|
-
if (ensureInitialized) {
|
|
2765
|
-
await ensureInitialized();
|
|
2766
|
-
}
|
|
2767
|
-
const page = await browserManager.getPage();
|
|
2768
2759
|
logger.info({ nodeId, format, scale }, "Capturing screenshot via Desktop Bridge");
|
|
2769
|
-
// Find the plugin UI frame and call the captureScreenshot function
|
|
2770
|
-
const frames = page.frames();
|
|
2771
2760
|
let result = null;
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2761
|
+
// Use the connector abstraction (supports both CDP and WebSocket)
|
|
2762
|
+
if (getDesktopConnector) {
|
|
2763
|
+
const connector = await getDesktopConnector();
|
|
2764
|
+
logger.info({ transport: connector.getTransportType?.() || 'unknown' }, "Screenshot via connector");
|
|
2765
|
+
result = await connector.captureScreenshot(nodeId || '', { format, scale });
|
|
2766
|
+
// Wrap in expected format only if connector returns raw data without a success flag
|
|
2767
|
+
if (result && typeof result.success === 'undefined' && result.image) {
|
|
2768
|
+
result = { success: true, image: result };
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
// Legacy CDP fallback (only when no connector factory is available)
|
|
2772
|
+
if (!result && !getDesktopConnector) {
|
|
2773
|
+
const browserManager = getBrowserManager?.();
|
|
2774
|
+
if (!browserManager) {
|
|
2775
|
+
throw new Error("Desktop Bridge not available. To capture screenshots:\n" +
|
|
2776
|
+
"1. Open your Figma file in Figma Desktop\n" +
|
|
2777
|
+
"2. Install and run the 'Figma Console MCP' plugin\n" +
|
|
2778
|
+
"3. Ensure the plugin shows 'MCP ready' status");
|
|
2779
2779
|
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2780
|
+
if (ensureInitialized) {
|
|
2781
|
+
await ensureInitialized();
|
|
2782
|
+
}
|
|
2783
|
+
const page = await browserManager.getPage();
|
|
2784
|
+
const frames = page.frames();
|
|
2785
|
+
for (const frame of frames) {
|
|
2786
|
+
try {
|
|
2787
|
+
const hasFunction = await frame.evaluate('typeof window.captureScreenshot === "function"');
|
|
2788
|
+
if (hasFunction) {
|
|
2789
|
+
result = await frame.evaluate(`window.captureScreenshot(${JSON.stringify(nodeId || '')}, ${JSON.stringify({ format, scale })})`);
|
|
2790
|
+
break;
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
catch {
|
|
2794
|
+
continue;
|
|
2795
|
+
}
|
|
2782
2796
|
}
|
|
2783
2797
|
}
|
|
2784
2798
|
if (!result) {
|
|
@@ -2850,33 +2864,39 @@ export function registerFigmaAPITools(server, getFigmaAPI, getCurrentUrl, getCon
|
|
|
2850
2864
|
"The tool automatically handles the #nodeId suffix for TEXT/BOOLEAN/INSTANCE_SWAP properties."),
|
|
2851
2865
|
}, async ({ nodeId, properties }) => {
|
|
2852
2866
|
try {
|
|
2853
|
-
// Check for Desktop Bridge connection
|
|
2854
|
-
const browserManager = getBrowserManager?.();
|
|
2855
|
-
if (!browserManager) {
|
|
2856
|
-
throw new Error("Desktop Bridge not available. To set instance properties:\n" +
|
|
2857
|
-
"1. Open your Figma file in Figma Desktop\n" +
|
|
2858
|
-
"2. Install and run the 'Figma Console MCP' plugin\n" +
|
|
2859
|
-
"3. Ensure the plugin shows 'MCP ready' status");
|
|
2860
|
-
}
|
|
2861
|
-
// Ensure browser is initialized
|
|
2862
|
-
if (ensureInitialized) {
|
|
2863
|
-
await ensureInitialized();
|
|
2864
|
-
}
|
|
2865
|
-
const page = await browserManager.getPage();
|
|
2866
2867
|
logger.info({ nodeId, properties: Object.keys(properties) }, "Setting instance properties via Desktop Bridge");
|
|
2867
|
-
// Find the plugin UI frame and call the setInstanceProperties function
|
|
2868
|
-
const frames = page.frames();
|
|
2869
2868
|
let result = null;
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2869
|
+
// Use the connector abstraction (supports both CDP and WebSocket)
|
|
2870
|
+
if (getDesktopConnector) {
|
|
2871
|
+
const connector = await getDesktopConnector();
|
|
2872
|
+
logger.info({ transport: connector.getTransportType?.() || 'unknown' }, "Instance properties via connector");
|
|
2873
|
+
result = await connector.setInstanceProperties(nodeId, properties);
|
|
2874
|
+
}
|
|
2875
|
+
// Legacy CDP fallback (only when no connector factory is available)
|
|
2876
|
+
if (!result && !getDesktopConnector) {
|
|
2877
|
+
const browserManager = getBrowserManager?.();
|
|
2878
|
+
if (!browserManager) {
|
|
2879
|
+
throw new Error("Desktop Bridge not available. To set instance properties:\n" +
|
|
2880
|
+
"1. Open your Figma file in Figma Desktop\n" +
|
|
2881
|
+
"2. Install and run the 'Figma Console MCP' plugin\n" +
|
|
2882
|
+
"3. Ensure the plugin shows 'MCP ready' status");
|
|
2883
|
+
}
|
|
2884
|
+
if (ensureInitialized) {
|
|
2885
|
+
await ensureInitialized();
|
|
2877
2886
|
}
|
|
2878
|
-
|
|
2879
|
-
|
|
2887
|
+
const page = await browserManager.getPage();
|
|
2888
|
+
const frames = page.frames();
|
|
2889
|
+
for (const frame of frames) {
|
|
2890
|
+
try {
|
|
2891
|
+
const hasFunction = await frame.evaluate('typeof window.setInstanceProperties === "function"');
|
|
2892
|
+
if (hasFunction) {
|
|
2893
|
+
result = await frame.evaluate(`window.setInstanceProperties(${JSON.stringify(nodeId)}, ${JSON.stringify(properties)})`);
|
|
2894
|
+
break;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
catch {
|
|
2898
|
+
continue;
|
|
2899
|
+
}
|
|
2880
2900
|
}
|
|
2881
2901
|
}
|
|
2882
2902
|
if (!result) {
|