@atezer/figma-mcp-bridge 1.2.0 → 1.2.2
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/CHANGELOG.md +62 -0
- package/README.md +99 -9
- package/dist/cloudflare/cloud-cors.js +40 -0
- package/dist/cloudflare/cloud-mode-kv.js +86 -0
- package/dist/cloudflare/cloud-mode-routes.js +97 -0
- package/dist/cloudflare/cloud-relay-session.js +141 -0
- package/dist/cloudflare/core/config.js +1 -1
- package/dist/cloudflare/core/figma-url.js +48 -0
- package/dist/cloudflare/core/plugin-bridge-connector.js +52 -43
- package/dist/cloudflare/core/plugin-bridge-server.js +211 -87
- package/dist/cloudflare/index.js +243 -4
- package/dist/core/config.js +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/figma-url.d.ts +10 -0
- package/dist/core/figma-url.d.ts.map +1 -0
- package/dist/core/figma-url.js +49 -0
- package/dist/core/figma-url.js.map +1 -0
- package/dist/core/plugin-bridge-connector.d.ts +6 -1
- package/dist/core/plugin-bridge-connector.d.ts.map +1 -1
- package/dist/core/plugin-bridge-connector.js +52 -43
- package/dist/core/plugin-bridge-connector.js.map +1 -1
- package/dist/core/plugin-bridge-server.d.ts +47 -14
- package/dist/core/plugin-bridge-server.d.ts.map +1 -1
- package/dist/core/plugin-bridge-server.js +211 -87
- package/dist/core/plugin-bridge-server.js.map +1 -1
- package/dist/local-plugin-only.d.ts.map +1 -1
- package/dist/local-plugin-only.js +163 -43
- package/dist/local-plugin-only.js.map +1 -1
- package/f-mcp-plugin/README.md +13 -5
- package/f-mcp-plugin/code.js +216 -2
- package/f-mcp-plugin/manifest.json +6 -2
- package/f-mcp-plugin/ui.html +694 -213
- package/package.json +7 -6
package/f-mcp-plugin/code.js
CHANGED
|
@@ -19,8 +19,15 @@ console.error = function() { _pushLog('error', arguments); _origError.apply(cons
|
|
|
19
19
|
|
|
20
20
|
console.log('🌉 [F-MCP ATezer Bridge] Plugin loaded and ready');
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
figma.showUI(__html__, { width:
|
|
22
|
+
// Start compact; UI asks for dynamic resize based on content.
|
|
23
|
+
figma.showUI(__html__, { width: 280, height: 96, visible: true, themeColors: true });
|
|
24
|
+
|
|
25
|
+
// Send file identity to UI so it can include it in the WebSocket handshake
|
|
26
|
+
figma.ui.postMessage({
|
|
27
|
+
type: 'FILE_IDENTITY',
|
|
28
|
+
fileKey: figma.fileKey || null,
|
|
29
|
+
fileName: figma.root.name || null
|
|
30
|
+
});
|
|
24
31
|
|
|
25
32
|
// Immediately fetch and send variables data to UI
|
|
26
33
|
(async () => {
|
|
@@ -165,6 +172,18 @@ function hexToFigmaRGB(hex) {
|
|
|
165
172
|
|
|
166
173
|
// Listen for requests from UI (e.g., component data requests, write operations)
|
|
167
174
|
figma.ui.onmessage = async (msg) => {
|
|
175
|
+
if (msg.type === 'RESIZE_UI') {
|
|
176
|
+
try {
|
|
177
|
+
var requestedWidth = Number(msg.width);
|
|
178
|
+
var requestedHeight = Number(msg.height);
|
|
179
|
+
var width = Math.max(220, Math.min(520, Math.round(isNaN(requestedWidth) ? 280 : requestedWidth)));
|
|
180
|
+
var height = Math.max(72, Math.min(420, Math.round(isNaN(requestedHeight) ? 96 : requestedHeight)));
|
|
181
|
+
figma.ui.resize(width, height);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.warn('🌉 [F-MCP ATezer Bridge] UI resize failed:', error && error.message ? error.message : String(error));
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
168
187
|
|
|
169
188
|
function rgbaToHex(color) {
|
|
170
189
|
if (!color || typeof color !== 'object') return null;
|
|
@@ -1146,6 +1165,201 @@ figma.ui.onmessage = async (msg) => {
|
|
|
1146
1165
|
}
|
|
1147
1166
|
}
|
|
1148
1167
|
|
|
1168
|
+
// ============================================================================
|
|
1169
|
+
// SEARCH_LIBRARY_ASSETS — enabled library variables + file components (query filter)
|
|
1170
|
+
// ============================================================================
|
|
1171
|
+
else if (msg.type === 'SEARCH_LIBRARY_ASSETS') {
|
|
1172
|
+
try {
|
|
1173
|
+
var q = (msg.query || '').toLowerCase().trim();
|
|
1174
|
+
var assetTypes = msg.assetTypes && msg.assetTypes.length ? msg.assetTypes : ['variables', 'components'];
|
|
1175
|
+
var limit = Math.min(Math.max(parseInt(msg.limit, 10) || 25, 1), 80);
|
|
1176
|
+
var currentPageOnly = msg.currentPageOnly !== false;
|
|
1177
|
+
var out = {
|
|
1178
|
+
success: true,
|
|
1179
|
+
query: msg.query || '',
|
|
1180
|
+
variables: [],
|
|
1181
|
+
components: [],
|
|
1182
|
+
componentSets: [],
|
|
1183
|
+
librariesScanned: [],
|
|
1184
|
+
notes: []
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
if (assetTypes.indexOf('variables') >= 0 && figma.teamLibrary && typeof figma.teamLibrary.getAvailableLibraryVariableCollectionsAsync === 'function') {
|
|
1188
|
+
try {
|
|
1189
|
+
var cols = await figma.teamLibrary.getAvailableLibraryVariableCollectionsAsync();
|
|
1190
|
+
for (var ci = 0; ci < cols.length && out.variables.length < limit; ci++) {
|
|
1191
|
+
var col = cols[ci];
|
|
1192
|
+
out.librariesScanned.push({ kind: 'variableCollection', name: col.name || '', key: col.key || '' });
|
|
1193
|
+
var libVars = await figma.teamLibrary.getVariablesInLibraryCollectionAsync(col.key);
|
|
1194
|
+
for (var vi = 0; vi < libVars.length && out.variables.length < limit; vi++) {
|
|
1195
|
+
var lv = libVars[vi];
|
|
1196
|
+
var nm = (lv.name || '').toLowerCase();
|
|
1197
|
+
if (!q || nm.indexOf(q) >= 0) {
|
|
1198
|
+
out.variables.push({
|
|
1199
|
+
name: lv.name,
|
|
1200
|
+
key: lv.key,
|
|
1201
|
+
resolvedType: lv.resolvedType,
|
|
1202
|
+
libraryCollectionKey: col.key,
|
|
1203
|
+
libraryCollectionName: col.name
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
} catch (ve) {
|
|
1209
|
+
out.variableLibraryError = ve && ve.message ? ve.message : String(ve);
|
|
1210
|
+
}
|
|
1211
|
+
} else if (assetTypes.indexOf('variables') >= 0) {
|
|
1212
|
+
out.notes.push('Library variables skipped: teamLibrary API unavailable or permission missing (manifest permissions: teamlibrary).');
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
if (assetTypes.indexOf('components') >= 0) {
|
|
1216
|
+
var components = [];
|
|
1217
|
+
var componentSets = [];
|
|
1218
|
+
var hitLimit = false;
|
|
1219
|
+
function extractComponentData(node, fromSet) {
|
|
1220
|
+
return {
|
|
1221
|
+
id: node.id,
|
|
1222
|
+
name: node.name,
|
|
1223
|
+
key: node.key,
|
|
1224
|
+
description: node.description || null,
|
|
1225
|
+
fromLibraryComponentSet: !!fromSet
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
function extractComponentSetData(node) {
|
|
1229
|
+
return {
|
|
1230
|
+
id: node.id,
|
|
1231
|
+
name: node.name,
|
|
1232
|
+
key: node.key,
|
|
1233
|
+
description: node.description || null,
|
|
1234
|
+
variantCount: node.children ? node.children.length : 0
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
async function processNodeList(nodes) {
|
|
1238
|
+
for (var i = 0; i < nodes.length && !hitLimit; i++) {
|
|
1239
|
+
if (components.length + componentSets.length >= limit) {
|
|
1240
|
+
hitLimit = true;
|
|
1241
|
+
break;
|
|
1242
|
+
}
|
|
1243
|
+
var node = nodes[i];
|
|
1244
|
+
if (!node) continue;
|
|
1245
|
+
if (node.loadAsync) await node.loadAsync();
|
|
1246
|
+
var nname = (node.name || '').toLowerCase();
|
|
1247
|
+
var ndesc = (node.description || '').toLowerCase();
|
|
1248
|
+
var match = !q || nname.indexOf(q) >= 0 || ndesc.indexOf(q) >= 0;
|
|
1249
|
+
if (node.type === 'COMPONENT_SET') {
|
|
1250
|
+
if (match) componentSets.push(extractComponentSetData(node));
|
|
1251
|
+
} else if (node.type === 'COMPONENT') {
|
|
1252
|
+
if (!node.parent || node.parent.type !== 'COMPONENT_SET') {
|
|
1253
|
+
if (match) components.push(extractComponentData(node, false));
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
if (currentPageOnly) {
|
|
1259
|
+
var page = figma.currentPage;
|
|
1260
|
+
if (page) {
|
|
1261
|
+
if (page.loadAsync) await page.loadAsync();
|
|
1262
|
+
var pn = page.findAllWithCriteria ? page.findAllWithCriteria({ types: ['COMPONENT', 'COMPONENT_SET'] }) : [];
|
|
1263
|
+
await processNodeList(pn);
|
|
1264
|
+
}
|
|
1265
|
+
} else {
|
|
1266
|
+
await figma.loadAllPagesAsync();
|
|
1267
|
+
var pages = figma.root.children;
|
|
1268
|
+
for (var p = 0; p < pages.length && !hitLimit; p++) {
|
|
1269
|
+
var pg = pages[p];
|
|
1270
|
+
if (pg && pg.loadAsync) await pg.loadAsync();
|
|
1271
|
+
var pageNodes = pg && pg.findAllWithCriteria ? pg.findAllWithCriteria({ types: ['COMPONENT', 'COMPONENT_SET'] }) : [];
|
|
1272
|
+
await processNodeList(pageNodes);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
out.components = components;
|
|
1276
|
+
out.componentSets = componentSets;
|
|
1277
|
+
out.truncatedByLimit = hitLimit;
|
|
1278
|
+
out.currentPageOnly = currentPageOnly;
|
|
1279
|
+
out.notes.push('Components are from the current file (local + published keys via component.key). Full remote catalog may require REST API.');
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
figma.ui.postMessage({
|
|
1283
|
+
type: 'SEARCH_LIBRARY_ASSETS_RESULT',
|
|
1284
|
+
requestId: msg.requestId,
|
|
1285
|
+
success: true,
|
|
1286
|
+
data: out
|
|
1287
|
+
});
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
var emsg = error && error.message ? error.message : String(error);
|
|
1290
|
+
figma.ui.postMessage({
|
|
1291
|
+
type: 'SEARCH_LIBRARY_ASSETS_RESULT',
|
|
1292
|
+
requestId: msg.requestId,
|
|
1293
|
+
success: false,
|
|
1294
|
+
error: emsg
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// ============================================================================
|
|
1300
|
+
// GET_CODE_CONNECT_HINTS — documentationLinks + component keys (not full Code Connect file map)
|
|
1301
|
+
// ============================================================================
|
|
1302
|
+
else if (msg.type === 'GET_CODE_CONNECT_HINTS') {
|
|
1303
|
+
try {
|
|
1304
|
+
var ids = msg.nodeIds && msg.nodeIds.length ? msg.nodeIds : [];
|
|
1305
|
+
var scanPage = msg.scanCurrentPage === true;
|
|
1306
|
+
var maxNodes = Math.min(parseInt(msg.maxNodes, 10) || 40, 120);
|
|
1307
|
+
var nodes = [];
|
|
1308
|
+
|
|
1309
|
+
async function pushNodeEntry(n) {
|
|
1310
|
+
if (!n) return;
|
|
1311
|
+
if (n.loadAsync) await n.loadAsync();
|
|
1312
|
+
var entry = { nodeId: n.id, name: n.name, type: n.type };
|
|
1313
|
+
if (n.type === 'COMPONENT' || n.type === 'COMPONENT_SET') {
|
|
1314
|
+
entry.componentKey = n.key;
|
|
1315
|
+
}
|
|
1316
|
+
if ('description' in n && n.description) entry.description = n.description;
|
|
1317
|
+
if ('documentationLinks' in n && n.documentationLinks && n.documentationLinks.length) {
|
|
1318
|
+
entry.documentationLinks = n.documentationLinks.map(function (link) {
|
|
1319
|
+
return { uri: link.uri, type: link.type };
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
nodes.push(entry);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
if (ids.length > 0) {
|
|
1326
|
+
for (var ii = 0; ii < ids.length && nodes.length < maxNodes; ii++) {
|
|
1327
|
+
var node = await figma.getNodeByIdAsync(ids[ii]);
|
|
1328
|
+
await pushNodeEntry(node);
|
|
1329
|
+
}
|
|
1330
|
+
} else if (scanPage) {
|
|
1331
|
+
var cur = figma.currentPage;
|
|
1332
|
+
if (cur && cur.loadAsync) await cur.loadAsync();
|
|
1333
|
+
var found = cur && cur.findAllWithCriteria
|
|
1334
|
+
? cur.findAllWithCriteria({ types: ['COMPONENT', 'COMPONENT_SET', 'INSTANCE'] })
|
|
1335
|
+
: [];
|
|
1336
|
+
for (var fi = 0; fi < found.length && nodes.length < maxNodes; fi++) {
|
|
1337
|
+
await pushNodeEntry(found[fi]);
|
|
1338
|
+
}
|
|
1339
|
+
} else {
|
|
1340
|
+
throw new Error('Provide nodeIds array or set scanCurrentPage=true');
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
figma.ui.postMessage({
|
|
1344
|
+
type: 'GET_CODE_CONNECT_HINTS_RESULT',
|
|
1345
|
+
requestId: msg.requestId,
|
|
1346
|
+
success: true,
|
|
1347
|
+
data: {
|
|
1348
|
+
nodes: nodes,
|
|
1349
|
+
note: 'Full Code Connect source paths live in repo figma.config / CLI; use Figma official MCP for native get_code_connect_map when needed.'
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
} catch (error2) {
|
|
1353
|
+
var emsg2 = error2 && error2.message ? error2.message : String(error2);
|
|
1354
|
+
figma.ui.postMessage({
|
|
1355
|
+
type: 'GET_CODE_CONNECT_HINTS_RESULT',
|
|
1356
|
+
requestId: msg.requestId,
|
|
1357
|
+
success: false,
|
|
1358
|
+
error: emsg2
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1149
1363
|
// ============================================================================
|
|
1150
1364
|
// INSTANTIATE_COMPONENT - Create a component instance with overrides
|
|
1151
1365
|
// ============================================================================
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
"editorType": ["figma", "figjam", "dev"],
|
|
8
8
|
"capabilities": ["inspect"],
|
|
9
9
|
"documentAccess": "dynamic-page",
|
|
10
|
+
"permissions": ["teamlibrary"],
|
|
11
|
+
"enablePrivatePluginApi": true,
|
|
10
12
|
"networkAccess": {
|
|
11
13
|
"allowedDomains": [
|
|
12
14
|
"http://localhost",
|
|
@@ -26,8 +28,10 @@
|
|
|
26
28
|
"http://localhost:5467", "ws://localhost:5467",
|
|
27
29
|
"http://localhost:5468", "ws://localhost:5468",
|
|
28
30
|
"http://localhost:5469", "ws://localhost:5469",
|
|
29
|
-
"http://localhost:5470", "ws://localhost:5470"
|
|
31
|
+
"http://localhost:5470", "ws://localhost:5470",
|
|
32
|
+
"https://figma-mcp-bridge.workers.dev",
|
|
33
|
+
"wss://figma-mcp-bridge.workers.dev"
|
|
30
34
|
],
|
|
31
|
-
"reasoning": "
|
|
35
|
+
"reasoning": "Local MCP WebSocket (5454–5470) and optional FMCP Cloud Mode (Workers); add your custom worker host here if different."
|
|
32
36
|
}
|
|
33
37
|
}
|