@mp3wizard/figma-console-mcp 1.23.1 → 1.27.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/README.md +49 -33
- package/dist/cloudflare/core/config.js +0 -8
- package/dist/cloudflare/core/console-monitor.js +3 -3
- package/dist/cloudflare/core/diagnose-tool.js +96 -0
- package/dist/cloudflare/core/figma-tools.js +69 -229
- package/dist/cloudflare/core/identity.js +96 -0
- package/dist/cloudflare/core/tokens/alias-resolver.js +98 -0
- package/dist/cloudflare/core/tokens/config.js +284 -0
- package/dist/cloudflare/core/tokens/figma-converter.js +195 -0
- package/dist/cloudflare/core/tokens/formatters/css-vars.js +329 -0
- package/dist/cloudflare/core/tokens/formatters/dtcg.js +300 -0
- package/dist/cloudflare/core/tokens/formatters/index.js +45 -0
- package/dist/cloudflare/core/tokens/formatters/json.js +7 -0
- package/dist/cloudflare/core/tokens/formatters/less.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/scss.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/stubs.js +11 -0
- package/dist/cloudflare/core/tokens/formatters/style-dictionary-v3.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/tailwind-v3.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/tailwind-v4.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/tokens-studio.js +4 -0
- package/dist/cloudflare/core/tokens/formatters/ts-module.js +4 -0
- package/dist/cloudflare/core/tokens/index.js +15 -0
- package/dist/cloudflare/core/tokens/parsers/css-vars.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/dtcg.js +253 -0
- package/dist/cloudflare/core/tokens/parsers/index.js +138 -0
- package/dist/cloudflare/core/tokens/parsers/json.js +7 -0
- package/dist/cloudflare/core/tokens/parsers/scss.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/stubs.js +13 -0
- package/dist/cloudflare/core/tokens/parsers/style-dictionary-v3.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tailwind-v3.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tailwind-v4.js +4 -0
- package/dist/cloudflare/core/tokens/parsers/tokens-studio.js +4 -0
- package/dist/cloudflare/core/tokens/schemas.js +148 -0
- package/dist/cloudflare/core/tokens/transforms/color.js +12 -0
- package/dist/cloudflare/core/tokens/transforms/index.js +29 -0
- package/dist/cloudflare/core/tokens/transforms/size.js +7 -0
- package/dist/cloudflare/core/tokens/types.js +18 -0
- package/dist/cloudflare/core/tokens-tools.js +849 -0
- package/dist/cloudflare/core/version-tools.js +151 -7
- package/dist/cloudflare/core/websocket-server.js +77 -55
- package/dist/cloudflare/index.js +37 -26
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +0 -8
- package/dist/core/config.js.map +1 -1
- package/dist/core/console-monitor.d.ts +2 -2
- package/dist/core/console-monitor.d.ts.map +1 -1
- package/dist/core/console-monitor.js +3 -3
- package/dist/core/console-monitor.js.map +1 -1
- package/dist/core/diagnose-tool.d.ts +33 -0
- package/dist/core/diagnose-tool.d.ts.map +1 -0
- package/dist/core/diagnose-tool.js +97 -0
- package/dist/core/diagnose-tool.js.map +1 -0
- package/dist/core/diff/diff-engine.d.ts +14 -0
- package/dist/core/diff/diff-engine.d.ts.map +1 -1
- package/dist/core/diff/diff-engine.js.map +1 -1
- package/dist/core/figma-connector.d.ts +1 -1
- package/dist/core/figma-connector.d.ts.map +1 -1
- package/dist/core/figma-tools.d.ts +1 -2
- package/dist/core/figma-tools.d.ts.map +1 -1
- package/dist/core/figma-tools.js +69 -229
- package/dist/core/figma-tools.js.map +1 -1
- package/dist/core/identity.d.ts +41 -0
- package/dist/core/identity.d.ts.map +1 -0
- package/dist/core/identity.js +97 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/tokens/alias-resolver.d.ts +40 -0
- package/dist/core/tokens/alias-resolver.d.ts.map +1 -0
- package/dist/core/tokens/alias-resolver.js +99 -0
- package/dist/core/tokens/alias-resolver.js.map +1 -0
- package/dist/core/tokens/config.d.ts +352 -0
- package/dist/core/tokens/config.d.ts.map +1 -0
- package/dist/core/tokens/config.js +285 -0
- package/dist/core/tokens/config.js.map +1 -0
- package/dist/core/tokens/figma-converter.d.ts +81 -0
- package/dist/core/tokens/figma-converter.d.ts.map +1 -0
- package/dist/core/tokens/figma-converter.js +196 -0
- package/dist/core/tokens/figma-converter.js.map +1 -0
- package/dist/core/tokens/formatters/css-vars.d.ts +24 -0
- package/dist/core/tokens/formatters/css-vars.d.ts.map +1 -0
- package/dist/core/tokens/formatters/css-vars.js +330 -0
- package/dist/core/tokens/formatters/css-vars.js.map +1 -0
- package/dist/core/tokens/formatters/dtcg.d.ts +28 -0
- package/dist/core/tokens/formatters/dtcg.d.ts.map +1 -0
- package/dist/core/tokens/formatters/dtcg.js +301 -0
- package/dist/core/tokens/formatters/dtcg.js.map +1 -0
- package/dist/core/tokens/formatters/index.d.ts +30 -0
- package/dist/core/tokens/formatters/index.d.ts.map +1 -0
- package/dist/core/tokens/formatters/index.js +46 -0
- package/dist/core/tokens/formatters/index.js.map +1 -0
- package/dist/core/tokens/formatters/json.d.ts +5 -0
- package/dist/core/tokens/formatters/json.d.ts.map +1 -0
- package/dist/core/tokens/formatters/json.js +8 -0
- package/dist/core/tokens/formatters/json.js.map +1 -0
- package/dist/core/tokens/formatters/less.d.ts +4 -0
- package/dist/core/tokens/formatters/less.d.ts.map +1 -0
- package/dist/core/tokens/formatters/less.js +5 -0
- package/dist/core/tokens/formatters/less.js.map +1 -0
- package/dist/core/tokens/formatters/scss.d.ts +4 -0
- package/dist/core/tokens/formatters/scss.d.ts.map +1 -0
- package/dist/core/tokens/formatters/scss.js +5 -0
- package/dist/core/tokens/formatters/scss.js.map +1 -0
- package/dist/core/tokens/formatters/stubs.d.ts +9 -0
- package/dist/core/tokens/formatters/stubs.d.ts.map +1 -0
- package/dist/core/tokens/formatters/stubs.js +12 -0
- package/dist/core/tokens/formatters/stubs.js.map +1 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.d.ts +4 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.d.ts.map +1 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.js +5 -0
- package/dist/core/tokens/formatters/style-dictionary-v3.js.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v3.d.ts +4 -0
- package/dist/core/tokens/formatters/tailwind-v3.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v3.js +5 -0
- package/dist/core/tokens/formatters/tailwind-v3.js.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v4.d.ts +4 -0
- package/dist/core/tokens/formatters/tailwind-v4.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tailwind-v4.js +5 -0
- package/dist/core/tokens/formatters/tailwind-v4.js.map +1 -0
- package/dist/core/tokens/formatters/tokens-studio.d.ts +4 -0
- package/dist/core/tokens/formatters/tokens-studio.d.ts.map +1 -0
- package/dist/core/tokens/formatters/tokens-studio.js +5 -0
- package/dist/core/tokens/formatters/tokens-studio.js.map +1 -0
- package/dist/core/tokens/formatters/ts-module.d.ts +4 -0
- package/dist/core/tokens/formatters/ts-module.d.ts.map +1 -0
- package/dist/core/tokens/formatters/ts-module.js +5 -0
- package/dist/core/tokens/formatters/ts-module.js.map +1 -0
- package/dist/core/tokens/index.d.ts +17 -0
- package/dist/core/tokens/index.d.ts.map +1 -0
- package/dist/core/tokens/index.js +16 -0
- package/dist/core/tokens/index.js.map +1 -0
- package/dist/core/tokens/parsers/css-vars.d.ts +3 -0
- package/dist/core/tokens/parsers/css-vars.d.ts.map +1 -0
- package/dist/core/tokens/parsers/css-vars.js +5 -0
- package/dist/core/tokens/parsers/css-vars.js.map +1 -0
- package/dist/core/tokens/parsers/dtcg.d.ts +21 -0
- package/dist/core/tokens/parsers/dtcg.d.ts.map +1 -0
- package/dist/core/tokens/parsers/dtcg.js +254 -0
- package/dist/core/tokens/parsers/dtcg.js.map +1 -0
- package/dist/core/tokens/parsers/index.d.ts +37 -0
- package/dist/core/tokens/parsers/index.d.ts.map +1 -0
- package/dist/core/tokens/parsers/index.js +139 -0
- package/dist/core/tokens/parsers/index.js.map +1 -0
- package/dist/core/tokens/parsers/json.d.ts +4 -0
- package/dist/core/tokens/parsers/json.d.ts.map +1 -0
- package/dist/core/tokens/parsers/json.js +8 -0
- package/dist/core/tokens/parsers/json.js.map +1 -0
- package/dist/core/tokens/parsers/scss.d.ts +3 -0
- package/dist/core/tokens/parsers/scss.d.ts.map +1 -0
- package/dist/core/tokens/parsers/scss.js +5 -0
- package/dist/core/tokens/parsers/scss.js.map +1 -0
- package/dist/core/tokens/parsers/stubs.d.ts +11 -0
- package/dist/core/tokens/parsers/stubs.d.ts.map +1 -0
- package/dist/core/tokens/parsers/stubs.js +14 -0
- package/dist/core/tokens/parsers/stubs.js.map +1 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.d.ts +3 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.d.ts.map +1 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.js +5 -0
- package/dist/core/tokens/parsers/style-dictionary-v3.js.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v3.d.ts +3 -0
- package/dist/core/tokens/parsers/tailwind-v3.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v3.js +5 -0
- package/dist/core/tokens/parsers/tailwind-v3.js.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v4.d.ts +3 -0
- package/dist/core/tokens/parsers/tailwind-v4.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tailwind-v4.js +5 -0
- package/dist/core/tokens/parsers/tailwind-v4.js.map +1 -0
- package/dist/core/tokens/parsers/tokens-studio.d.ts +3 -0
- package/dist/core/tokens/parsers/tokens-studio.d.ts.map +1 -0
- package/dist/core/tokens/parsers/tokens-studio.js +5 -0
- package/dist/core/tokens/parsers/tokens-studio.js.map +1 -0
- package/dist/core/tokens/schemas.d.ts +152 -0
- package/dist/core/tokens/schemas.d.ts.map +1 -0
- package/dist/core/tokens/schemas.js +149 -0
- package/dist/core/tokens/schemas.js.map +1 -0
- package/dist/core/tokens/transforms/color.d.ts +9 -0
- package/dist/core/tokens/transforms/color.d.ts.map +1 -0
- package/dist/core/tokens/transforms/color.js +13 -0
- package/dist/core/tokens/transforms/color.js.map +1 -0
- package/dist/core/tokens/transforms/index.d.ts +36 -0
- package/dist/core/tokens/transforms/index.d.ts.map +1 -0
- package/dist/core/tokens/transforms/index.js +30 -0
- package/dist/core/tokens/transforms/index.js.map +1 -0
- package/dist/core/tokens/transforms/size.d.ts +7 -0
- package/dist/core/tokens/transforms/size.d.ts.map +1 -0
- package/dist/core/tokens/transforms/size.js +8 -0
- package/dist/core/tokens/transforms/size.js.map +1 -0
- package/dist/core/tokens/types.d.ts +228 -0
- package/dist/core/tokens/types.d.ts.map +1 -0
- package/dist/core/tokens/types.js +19 -0
- package/dist/core/tokens/types.js.map +1 -0
- package/dist/core/tokens-tools.d.ts +42 -0
- package/dist/core/tokens-tools.d.ts.map +1 -0
- package/dist/core/tokens-tools.js +850 -0
- package/dist/core/tokens-tools.js.map +1 -0
- package/dist/core/types/index.d.ts +0 -8
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/version-tools.d.ts +30 -1
- package/dist/core/version-tools.d.ts.map +1 -1
- package/dist/core/version-tools.js +151 -7
- package/dist/core/version-tools.js.map +1 -1
- package/dist/core/websocket-connector.d.ts +1 -1
- package/dist/core/websocket-connector.d.ts.map +1 -1
- package/dist/core/websocket-server.d.ts +47 -3
- package/dist/core/websocket-server.d.ts.map +1 -1
- package/dist/core/websocket-server.js +77 -55
- package/dist/core/websocket-server.js.map +1 -1
- package/dist/local.d.ts +0 -12
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +967 -3406
- package/dist/local.js.map +1 -1
- package/figma-desktop-bridge/code.js +59 -63
- package/figma-desktop-bridge/ui.html +85 -11
- package/package.json +12 -30
- package/figma-desktop-bridge/ui-full.html +0 -1353
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
// Figma Desktop Bridge - MCP Plugin
|
|
2
|
-
// Bridges Figma API to MCP clients via plugin UI
|
|
3
|
-
// Supports: Variables, Components, Styles, and more
|
|
4
|
-
// Uses postMessage to communicate with
|
|
5
|
-
//
|
|
2
|
+
// Bridges the Figma Plugin API to MCP clients via the plugin's UI iframe.
|
|
3
|
+
// Supports: Variables, Components, Styles, and more.
|
|
4
|
+
// Uses postMessage to communicate with ui.html (bypassing worker sandbox limitations),
|
|
5
|
+
// which then forwards messages to the MCP server over the WebSocket bridge.
|
|
6
6
|
|
|
7
7
|
// Plugin version — sent in FILE_INFO for server-side version compatibility checks.
|
|
8
8
|
// The server compares this against its own version to detect stale cached plugins.
|
|
9
|
-
var PLUGIN_VERSION = '1.
|
|
9
|
+
var PLUGIN_VERSION = '1.27.1'; // Kept in sync with package.json by scripts/release.sh — see issue #62.
|
|
10
10
|
|
|
11
11
|
console.log('🌉 [Desktop Bridge] Plugin loaded (v' + PLUGIN_VERSION + ')');
|
|
12
12
|
|
|
13
13
|
// Show minimal UI - compact status indicator
|
|
14
|
-
figma.showUI(__html__, { width:
|
|
14
|
+
figma.showUI(__html__, { width: 180, height: 50, visible: true, themeColors: true });
|
|
15
15
|
|
|
16
16
|
// ============================================================================
|
|
17
17
|
// CONSOLE CAPTURE — Intercept console.* in the QuickJS sandbox and forward
|
|
18
18
|
// to ui.html via postMessage so the WebSocket bridge can relay them to the MCP
|
|
19
|
-
// server. This
|
|
19
|
+
// server. This is the only console-capture path in local mode (no browser
|
|
20
|
+
// process is involved).
|
|
20
21
|
// ============================================================================
|
|
21
22
|
(function() {
|
|
22
23
|
var levels = ['log', 'info', 'warn', 'error', 'debug'];
|
|
@@ -220,59 +221,6 @@ function hexToFigmaRGB(hex) {
|
|
|
220
221
|
// Listen for requests from UI (e.g., component data requests, write operations)
|
|
221
222
|
figma.ui.onmessage = async (msg) => {
|
|
222
223
|
|
|
223
|
-
// ============================================================================
|
|
224
|
-
// BOOT_LOAD_UI - Bootloader fetched fresh UI HTML from the MCP server.
|
|
225
|
-
// Replace the bootloader with the full, always-up-to-date plugin UI.
|
|
226
|
-
// This uses figma.showUI() with the HTML string directly — no redirects,
|
|
227
|
-
// no cross-origin, no CSP issues.
|
|
228
|
-
// ============================================================================
|
|
229
|
-
if (msg.type === 'BOOT_LOAD_UI' && msg.html) {
|
|
230
|
-
console.log('🌉 [Desktop Bridge] Bootloader delivered fresh UI (' + msg.html.length + ' bytes), loading...');
|
|
231
|
-
figma.showUI(msg.html, { width: 140, height: 50, visible: true, themeColors: true });
|
|
232
|
-
|
|
233
|
-
// Re-send variables data to the fresh UI — the original send went to the
|
|
234
|
-
// bootloader which discarded it. The fresh UI needs it to show "ready" status.
|
|
235
|
-
(async function() {
|
|
236
|
-
try {
|
|
237
|
-
var variables = await figma.variables.getLocalVariablesAsync();
|
|
238
|
-
var collections = await figma.variables.getLocalVariableCollectionsAsync();
|
|
239
|
-
figma.ui.postMessage({
|
|
240
|
-
type: 'VARIABLES_DATA',
|
|
241
|
-
data: {
|
|
242
|
-
success: true,
|
|
243
|
-
timestamp: Date.now(),
|
|
244
|
-
fileKey: figma.fileKey || null,
|
|
245
|
-
variables: variables.map(function(v) { return {
|
|
246
|
-
id: v.id, name: v.name, key: v.key, resolvedType: v.resolvedType,
|
|
247
|
-
valuesByMode: v.valuesByMode, variableCollectionId: v.variableCollectionId,
|
|
248
|
-
scopes: v.scopes, codeSyntax: v.codeSyntax || {}, description: v.description, hiddenFromPublishing: v.hiddenFromPublishing
|
|
249
|
-
}; }),
|
|
250
|
-
variableCollections: collections.map(function(c) { return {
|
|
251
|
-
id: c.id, name: c.name, key: c.key, modes: c.modes,
|
|
252
|
-
defaultModeId: c.defaultModeId, variableIds: c.variableIds
|
|
253
|
-
}; })
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
console.log('🌉 [Desktop Bridge] Re-sent variables to fresh UI (' + variables.length + ' vars)');
|
|
257
|
-
} catch (e) {
|
|
258
|
-
console.log('🌉 [Desktop Bridge] Could not re-send variables:', e.message || e);
|
|
259
|
-
}
|
|
260
|
-
})();
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// ============================================================================
|
|
265
|
-
// BOOT_FALLBACK - Bootloader found an old server that doesn't support the
|
|
266
|
-
// bootloader protocol. Fall back to reloading the cached __html__ which
|
|
267
|
-
// contains the full UI (for users who haven't switched to the bootloader yet,
|
|
268
|
-
// __html__ IS the full UI; for bootloader users, this is a no-op reload).
|
|
269
|
-
// ============================================================================
|
|
270
|
-
if (msg.type === 'BOOT_FALLBACK') {
|
|
271
|
-
console.log('🌉 [Desktop Bridge] Old server detected on port ' + msg.port + ', using cached UI');
|
|
272
|
-
figma.showUI(__html__, { width: 140, height: 50, visible: true, themeColors: true });
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
224
|
// ============================================================================
|
|
277
225
|
// EXECUTE_CODE - Arbitrary code execution (Power Tool)
|
|
278
226
|
// ============================================================================
|
|
@@ -376,7 +324,7 @@ figma.ui.onmessage = async (msg) => {
|
|
|
376
324
|
var errorMsg = error && error.message ? error.message : String(error);
|
|
377
325
|
var errorStack = error && error.stack ? error.stack : '';
|
|
378
326
|
|
|
379
|
-
// Log error details as strings so they
|
|
327
|
+
// Log error details as strings so they survive intact through the WebSocket bridge into figma_get_console_logs
|
|
380
328
|
console.error('🌉 [Desktop Bridge] Code execution error: [' + errorName + '] ' + errorMsg);
|
|
381
329
|
if (errorStack) {
|
|
382
330
|
console.error('🌉 [Desktop Bridge] Stack:', errorStack);
|
|
@@ -3101,7 +3049,7 @@ figma.ui.onmessage = async (msg) => {
|
|
|
3101
3049
|
});
|
|
3102
3050
|
// Short delay to let the response message be sent before reload
|
|
3103
3051
|
setTimeout(function() {
|
|
3104
|
-
figma.showUI(__html__, { width:
|
|
3052
|
+
figma.showUI(__html__, { width: 180, height: 50, visible: true, themeColors: true });
|
|
3105
3053
|
}, 100);
|
|
3106
3054
|
} catch (error) {
|
|
3107
3055
|
var errorMsg = error && error.message ? error.message : String(error);
|
|
@@ -6299,6 +6247,12 @@ figma.loadAllPagesAsync().then(function() {
|
|
|
6299
6247
|
var hasNodeChanges = false;
|
|
6300
6248
|
var changedNodeIds = [];
|
|
6301
6249
|
|
|
6250
|
+
// v1.25.0: also collect metadata-only changes (descriptions, annotations).
|
|
6251
|
+
// These are properties that the REST API DOESN'T expose in version snapshots,
|
|
6252
|
+
// so figma_diff_versions can't see them through REST alone. Forwarding them
|
|
6253
|
+
// via the Plugin API + WebSocket is the only way to make them diff-visible.
|
|
6254
|
+
var metadataChanges = [];
|
|
6255
|
+
|
|
6302
6256
|
for (var i = 0; i < event.documentChanges.length; i++) {
|
|
6303
6257
|
var change = event.documentChanges[i];
|
|
6304
6258
|
if (change.type === 'STYLE_CREATE' || change.type === 'STYLE_DELETE' || change.type === 'STYLE_PROPERTY_CHANGE') {
|
|
@@ -6309,6 +6263,48 @@ figma.loadAllPagesAsync().then(function() {
|
|
|
6309
6263
|
changedNodeIds.push(change.id);
|
|
6310
6264
|
}
|
|
6311
6265
|
}
|
|
6266
|
+
|
|
6267
|
+
// Metadata change detection. PROPERTY_CHANGE includes a `properties` array
|
|
6268
|
+
// listing which fields changed; we only snapshot the new value for the
|
|
6269
|
+
// two fields Figma's REST never returns: `description` and `annotations`.
|
|
6270
|
+
if (change.type === 'PROPERTY_CHANGE' && change.node && Array.isArray(change.properties)) {
|
|
6271
|
+
for (var p = 0; p < change.properties.length; p++) {
|
|
6272
|
+
var prop = change.properties[p];
|
|
6273
|
+
if (prop === 'description' || prop === 'descriptionMarkdown' || prop === 'annotations') {
|
|
6274
|
+
try {
|
|
6275
|
+
var newValue = change.node[prop];
|
|
6276
|
+
// Serialize annotations to plain JSON-safe form
|
|
6277
|
+
if (prop === 'annotations' && Array.isArray(newValue)) {
|
|
6278
|
+
newValue = newValue.map(function(a) {
|
|
6279
|
+
return {
|
|
6280
|
+
label: a.label || null,
|
|
6281
|
+
labelMarkdown: a.labelMarkdown || null,
|
|
6282
|
+
categoryId: a.categoryId || null,
|
|
6283
|
+
properties: a.properties || []
|
|
6284
|
+
};
|
|
6285
|
+
});
|
|
6286
|
+
}
|
|
6287
|
+
metadataChanges.push({
|
|
6288
|
+
node_id: change.node.id,
|
|
6289
|
+
node_name: change.node.name || null,
|
|
6290
|
+
node_type: change.node.type || null,
|
|
6291
|
+
field: prop === 'descriptionMarkdown' ? 'description' : prop,
|
|
6292
|
+
new_value: newValue,
|
|
6293
|
+
timestamp: Date.now()
|
|
6294
|
+
});
|
|
6295
|
+
} catch (e) {
|
|
6296
|
+
// Some property reads can throw on detached / deleted nodes
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
6300
|
+
}
|
|
6301
|
+
}
|
|
6302
|
+
|
|
6303
|
+
if (metadataChanges.length > 0) {
|
|
6304
|
+
figma.ui.postMessage({
|
|
6305
|
+
type: 'METADATA_CHANGE',
|
|
6306
|
+
data: { changes: metadataChanges }
|
|
6307
|
+
});
|
|
6312
6308
|
}
|
|
6313
6309
|
|
|
6314
6310
|
if (hasStyleChanges || hasNodeChanges) {
|
|
@@ -6376,4 +6372,4 @@ console.log('🌉 [Desktop Bridge] Ready to handle component requests');
|
|
|
6376
6372
|
console.log('🌉 [Desktop Bridge] Plugin will stay open until manually closed');
|
|
6377
6373
|
|
|
6378
6374
|
// Plugin stays open - no auto-close
|
|
6379
|
-
// UI iframe remains accessible
|
|
6375
|
+
// UI iframe remains accessible so the in-iframe WebSocket bridge client can keep relaying state to the MCP server
|
|
@@ -217,7 +217,7 @@
|
|
|
217
217
|
|
|
218
218
|
<script>
|
|
219
219
|
// ============================================================================
|
|
220
|
-
// GLOBAL STATE
|
|
220
|
+
// GLOBAL STATE — backing store for the in-iframe WebSocket bridge client
|
|
221
221
|
// ============================================================================
|
|
222
222
|
window.__figmaVariablesData = null;
|
|
223
223
|
window.__figmaVariablesReady = false;
|
|
@@ -227,13 +227,67 @@
|
|
|
227
227
|
|
|
228
228
|
let requestIdCounter = 0;
|
|
229
229
|
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
// Status pill state — captured here so we can re-render whenever the
|
|
231
|
+
// mode (local port count, cloud paired) changes without losing the
|
|
232
|
+
// ready/connecting/error state set by the data layer.
|
|
233
|
+
let _currentStatusState = 'connecting';
|
|
234
|
+
let _currentStatusActive = false;
|
|
235
|
+
let _currentStatusError = false;
|
|
236
|
+
|
|
237
|
+
// Compute the mode label shown in place of the static "MCP" prefix.
|
|
238
|
+
// Returns "Local", "Cloud", "Local + Cloud", or "" (unknown — falls back
|
|
239
|
+
// to the original "MCP" label so the pill always says something).
|
|
240
|
+
// Lets users see at a glance which transport is carrying their session,
|
|
241
|
+
// so a confused user doesn't have to guess whether they're talking to
|
|
242
|
+
// the local server, the cloud relay, or both. Port numbers are omitted
|
|
243
|
+
// here to keep the pill compact — figma_diagnose exposes the port when
|
|
244
|
+
// it's actually needed.
|
|
245
|
+
//
|
|
246
|
+
// NOTE: `activeConnections` is declared inside the connection-pool IIFE
|
|
247
|
+
// below (around line ~617), so it is NOT in scope from this top-level
|
|
248
|
+
// function. The IIFE exposes `window.__wsGetActiveConnections()` for
|
|
249
|
+
// exactly this reason — calling that getter is what makes the pill
|
|
250
|
+
// update once the pool reports a live connection.
|
|
251
|
+
function renderModeText() {
|
|
252
|
+
try {
|
|
253
|
+
var getConns = window.__wsGetActiveConnections;
|
|
254
|
+
var conns = typeof getConns === 'function' ? (getConns() || []) : [];
|
|
255
|
+
var localUp = conns.some(function(c) {
|
|
256
|
+
return c && c.ws && c.ws.readyState === 1 && c.port !== 'cloud';
|
|
257
|
+
});
|
|
258
|
+
var cloudUp = conns.some(function(c) {
|
|
259
|
+
return c && c.ws && c.ws.readyState === 1 && c.port === 'cloud';
|
|
260
|
+
});
|
|
261
|
+
if (localUp && cloudUp) return 'Local + Cloud';
|
|
262
|
+
if (localUp) return 'Local';
|
|
263
|
+
if (cloudUp) return 'Cloud';
|
|
264
|
+
return '';
|
|
265
|
+
} catch (e) {
|
|
266
|
+
return '';
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function refreshStatus() {
|
|
271
|
+
var dot = document.getElementById('status-dot');
|
|
272
|
+
var stateText = document.getElementById('status-state');
|
|
273
|
+
var labelEl = document.querySelector('.status-text .label');
|
|
274
|
+
if (!dot || !stateText) return;
|
|
275
|
+
dot.className = 'status-indicator ' + (_currentStatusError ? 'error' : (_currentStatusActive ? 'active' : 'loading'));
|
|
276
|
+
var mode = renderModeText();
|
|
277
|
+
// Replace the static "MCP" label with the live mode label (Local / Cloud
|
|
278
|
+
// / Local + Cloud). Fall back to "MCP" when nothing is connected yet so
|
|
279
|
+
// the pill still has a visible label during initial scan.
|
|
280
|
+
if (labelEl) labelEl.textContent = mode || 'MCP';
|
|
281
|
+
stateText.textContent = _currentStatusState;
|
|
282
|
+
}
|
|
234
283
|
|
|
235
|
-
|
|
236
|
-
|
|
284
|
+
// UI update helper — preserves the latest state and re-renders, picking
|
|
285
|
+
// up any mode-text changes (local/cloud connect/disconnect).
|
|
286
|
+
function updateStatus(state, isActive, isError) {
|
|
287
|
+
_currentStatusState = state;
|
|
288
|
+
_currentStatusActive = isActive;
|
|
289
|
+
_currentStatusError = isError;
|
|
290
|
+
refreshStatus();
|
|
237
291
|
}
|
|
238
292
|
|
|
239
293
|
// ============================================================================
|
|
@@ -605,7 +659,8 @@
|
|
|
605
659
|
};
|
|
606
660
|
|
|
607
661
|
// ============================================================================
|
|
608
|
-
// WEBSOCKET BRIDGE CLIENT
|
|
662
|
+
// WEBSOCKET BRIDGE CLIENT — primary transport from the plugin sandbox to
|
|
663
|
+
// the MCP server. Scans port range 9223–9232 for multiple instances.
|
|
609
664
|
// ============================================================================
|
|
610
665
|
(function() {
|
|
611
666
|
// Port range for multi-instance support (matches server's port-discovery.ts)
|
|
@@ -800,7 +855,9 @@
|
|
|
800
855
|
}
|
|
801
856
|
|
|
802
857
|
/**
|
|
803
|
-
* Update backward-compat variables (ws, wsPort, wsConnected)
|
|
858
|
+
* Update backward-compat variables (ws, wsPort, wsConnected) and
|
|
859
|
+
* re-render the status pill so the Local/Cloud mode suffix reflects
|
|
860
|
+
* the new connection set.
|
|
804
861
|
*/
|
|
805
862
|
function updateCompatState() {
|
|
806
863
|
var live = activeConnections.filter(function(c) { return c.ws.readyState === 1; });
|
|
@@ -812,6 +869,9 @@
|
|
|
812
869
|
ws = null;
|
|
813
870
|
wsPort = null;
|
|
814
871
|
}
|
|
872
|
+
// refreshStatus is defined in the outer scope (top-level script block).
|
|
873
|
+
// Guarded for the unlikely case it's invoked before that script runs.
|
|
874
|
+
if (typeof refreshStatus === 'function') refreshStatus();
|
|
815
875
|
}
|
|
816
876
|
|
|
817
877
|
/**
|
|
@@ -1043,6 +1103,14 @@
|
|
|
1043
1103
|
}
|
|
1044
1104
|
};
|
|
1045
1105
|
|
|
1106
|
+
// v1.25.0: forward METADATA_CHANGE events (description/annotation edits)
|
|
1107
|
+
// so figma_diff_versions can surface them despite Figma REST not exposing them.
|
|
1108
|
+
window.__wsForwardMetadataChange = function(data) {
|
|
1109
|
+
if (wsConnected) {
|
|
1110
|
+
broadcastToAll({ type: 'METADATA_CHANGE', data: data });
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1046
1114
|
// Forward CONSOLE_CAPTURE events to all servers for console monitoring
|
|
1047
1115
|
window.__wsForwardConsoleCapture = function(data) {
|
|
1048
1116
|
if (wsConnected) {
|
|
@@ -1101,9 +1169,10 @@
|
|
|
1101
1169
|
section.classList.toggle('visible');
|
|
1102
1170
|
toggle.classList.toggle('expanded');
|
|
1103
1171
|
|
|
1104
|
-
// Resize plugin window to fit content
|
|
1172
|
+
// Resize plugin window to fit content. Default width is 180px to give
|
|
1173
|
+
// the status pill enough room for the "Local + Cloud" mode label.
|
|
1105
1174
|
var height = isExpanding ? 130 : 50;
|
|
1106
|
-
parent.postMessage({ pluginMessage: { type: 'RESIZE_UI', width:
|
|
1175
|
+
parent.postMessage({ pluginMessage: { type: 'RESIZE_UI', width: 180, height: height } }, '*');
|
|
1107
1176
|
}
|
|
1108
1177
|
|
|
1109
1178
|
function resetCloudUI() {
|
|
@@ -1472,6 +1541,11 @@
|
|
|
1472
1541
|
if (window.__wsForwardDocumentChange) window.__wsForwardDocumentChange(msg.data);
|
|
1473
1542
|
break;
|
|
1474
1543
|
|
|
1544
|
+
// v1.25.0: metadata change events (description/annotation edits via Plugin API)
|
|
1545
|
+
case 'METADATA_CHANGE':
|
|
1546
|
+
if (window.__wsForwardMetadataChange) window.__wsForwardMetadataChange(msg.data);
|
|
1547
|
+
break;
|
|
1548
|
+
|
|
1475
1549
|
// Console capture events (for console monitoring via WebSocket)
|
|
1476
1550
|
case 'CONSOLE_CAPTURE':
|
|
1477
1551
|
if (window.__wsForwardConsoleCapture) window.__wsForwardConsoleCapture(msg);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mp3wizard/figma-console-mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MCP server for
|
|
3
|
+
"version": "1.27.2",
|
|
4
|
+
"description": "The most comprehensive MCP server for Figma — design tokens, variables, components, write tools, version history diff, accessibility audits, FigJam, Slides, and more. Local (WebSocket Desktop Bridge plugin) and Cloudflare Workers (paired + remote) modes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/local.js",
|
|
7
7
|
"types": "dist/local.d.ts",
|
|
@@ -37,15 +37,18 @@
|
|
|
37
37
|
"mcp",
|
|
38
38
|
"figma",
|
|
39
39
|
"plugin",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
40
|
+
"design-tokens",
|
|
41
|
+
"design-systems",
|
|
42
|
+
"variables",
|
|
43
|
+
"figjam",
|
|
44
|
+
"slides",
|
|
45
|
+
"accessibility",
|
|
42
46
|
"ai",
|
|
43
47
|
"anthropic",
|
|
44
48
|
"claude",
|
|
45
|
-
"cloudflare"
|
|
46
|
-
"workers"
|
|
49
|
+
"cloudflare"
|
|
47
50
|
],
|
|
48
|
-
"author": "
|
|
51
|
+
"author": "Southleft",
|
|
49
52
|
"license": "MIT",
|
|
50
53
|
"repository": {
|
|
51
54
|
"type": "git",
|
|
@@ -60,12 +63,10 @@
|
|
|
60
63
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
61
64
|
"agents": "^0.7.1",
|
|
62
65
|
"axe-core": "^4.11.2",
|
|
63
|
-
"chrome-remote-interface": "^0.33.2",
|
|
64
66
|
"jsdom": "^29.0.1",
|
|
65
67
|
"pino": "^9.5.0",
|
|
66
68
|
"pino-pretty": "^13.0.0",
|
|
67
|
-
"
|
|
68
|
-
"uuid": ">=11.1.1",
|
|
69
|
+
"uuid": "^11.0.3",
|
|
69
70
|
"ws": "^8.19.0",
|
|
70
71
|
"zod": "^3.25.76"
|
|
71
72
|
},
|
|
@@ -81,28 +82,9 @@
|
|
|
81
82
|
"ts-jest": "^29.2.5",
|
|
82
83
|
"tsx": "^4.19.2",
|
|
83
84
|
"typescript": "5.9.3",
|
|
84
|
-
"vite": "
|
|
85
|
+
"vite": "^6.0.0",
|
|
85
86
|
"vite-plugin-singlefile": "^2.0.0",
|
|
86
87
|
"wrangler": "^4.42.0",
|
|
87
88
|
"zod-to-json-schema": "^3.25.1"
|
|
88
|
-
},
|
|
89
|
-
"overrides": {
|
|
90
|
-
"path-to-regexp": ">=8.4.1",
|
|
91
|
-
"miniflare": {
|
|
92
|
-
"undici": ">=7.24.0"
|
|
93
|
-
},
|
|
94
|
-
"handlebars": ">=4.7.9",
|
|
95
|
-
"brace-expansion": ">=1.1.13",
|
|
96
|
-
"lodash": ">=4.18.0",
|
|
97
|
-
"picomatch": ">=4.0.4",
|
|
98
|
-
"hono": ">=4.12.18",
|
|
99
|
-
"@hono/node-server": ">=1.19.13",
|
|
100
|
-
"basic-ftp": ">=5.3.0",
|
|
101
|
-
"postcss": ">=8.5.10",
|
|
102
|
-
"fast-uri": ">=3.1.2",
|
|
103
|
-
"ip-address": ">=10.1.1",
|
|
104
|
-
"vite": {
|
|
105
|
-
"picomatch": ">=4.0.4"
|
|
106
|
-
}
|
|
107
89
|
}
|
|
108
90
|
}
|