@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.
Files changed (213) hide show
  1. package/README.md +49 -33
  2. package/dist/cloudflare/core/config.js +0 -8
  3. package/dist/cloudflare/core/console-monitor.js +3 -3
  4. package/dist/cloudflare/core/diagnose-tool.js +96 -0
  5. package/dist/cloudflare/core/figma-tools.js +69 -229
  6. package/dist/cloudflare/core/identity.js +96 -0
  7. package/dist/cloudflare/core/tokens/alias-resolver.js +98 -0
  8. package/dist/cloudflare/core/tokens/config.js +284 -0
  9. package/dist/cloudflare/core/tokens/figma-converter.js +195 -0
  10. package/dist/cloudflare/core/tokens/formatters/css-vars.js +329 -0
  11. package/dist/cloudflare/core/tokens/formatters/dtcg.js +300 -0
  12. package/dist/cloudflare/core/tokens/formatters/index.js +45 -0
  13. package/dist/cloudflare/core/tokens/formatters/json.js +7 -0
  14. package/dist/cloudflare/core/tokens/formatters/less.js +4 -0
  15. package/dist/cloudflare/core/tokens/formatters/scss.js +4 -0
  16. package/dist/cloudflare/core/tokens/formatters/stubs.js +11 -0
  17. package/dist/cloudflare/core/tokens/formatters/style-dictionary-v3.js +4 -0
  18. package/dist/cloudflare/core/tokens/formatters/tailwind-v3.js +4 -0
  19. package/dist/cloudflare/core/tokens/formatters/tailwind-v4.js +4 -0
  20. package/dist/cloudflare/core/tokens/formatters/tokens-studio.js +4 -0
  21. package/dist/cloudflare/core/tokens/formatters/ts-module.js +4 -0
  22. package/dist/cloudflare/core/tokens/index.js +15 -0
  23. package/dist/cloudflare/core/tokens/parsers/css-vars.js +4 -0
  24. package/dist/cloudflare/core/tokens/parsers/dtcg.js +253 -0
  25. package/dist/cloudflare/core/tokens/parsers/index.js +138 -0
  26. package/dist/cloudflare/core/tokens/parsers/json.js +7 -0
  27. package/dist/cloudflare/core/tokens/parsers/scss.js +4 -0
  28. package/dist/cloudflare/core/tokens/parsers/stubs.js +13 -0
  29. package/dist/cloudflare/core/tokens/parsers/style-dictionary-v3.js +4 -0
  30. package/dist/cloudflare/core/tokens/parsers/tailwind-v3.js +4 -0
  31. package/dist/cloudflare/core/tokens/parsers/tailwind-v4.js +4 -0
  32. package/dist/cloudflare/core/tokens/parsers/tokens-studio.js +4 -0
  33. package/dist/cloudflare/core/tokens/schemas.js +148 -0
  34. package/dist/cloudflare/core/tokens/transforms/color.js +12 -0
  35. package/dist/cloudflare/core/tokens/transforms/index.js +29 -0
  36. package/dist/cloudflare/core/tokens/transforms/size.js +7 -0
  37. package/dist/cloudflare/core/tokens/types.js +18 -0
  38. package/dist/cloudflare/core/tokens-tools.js +849 -0
  39. package/dist/cloudflare/core/version-tools.js +151 -7
  40. package/dist/cloudflare/core/websocket-server.js +77 -55
  41. package/dist/cloudflare/index.js +37 -26
  42. package/dist/core/config.d.ts.map +1 -1
  43. package/dist/core/config.js +0 -8
  44. package/dist/core/config.js.map +1 -1
  45. package/dist/core/console-monitor.d.ts +2 -2
  46. package/dist/core/console-monitor.d.ts.map +1 -1
  47. package/dist/core/console-monitor.js +3 -3
  48. package/dist/core/console-monitor.js.map +1 -1
  49. package/dist/core/diagnose-tool.d.ts +33 -0
  50. package/dist/core/diagnose-tool.d.ts.map +1 -0
  51. package/dist/core/diagnose-tool.js +97 -0
  52. package/dist/core/diagnose-tool.js.map +1 -0
  53. package/dist/core/diff/diff-engine.d.ts +14 -0
  54. package/dist/core/diff/diff-engine.d.ts.map +1 -1
  55. package/dist/core/diff/diff-engine.js.map +1 -1
  56. package/dist/core/figma-connector.d.ts +1 -1
  57. package/dist/core/figma-connector.d.ts.map +1 -1
  58. package/dist/core/figma-tools.d.ts +1 -2
  59. package/dist/core/figma-tools.d.ts.map +1 -1
  60. package/dist/core/figma-tools.js +69 -229
  61. package/dist/core/figma-tools.js.map +1 -1
  62. package/dist/core/identity.d.ts +41 -0
  63. package/dist/core/identity.d.ts.map +1 -0
  64. package/dist/core/identity.js +97 -0
  65. package/dist/core/identity.js.map +1 -0
  66. package/dist/core/tokens/alias-resolver.d.ts +40 -0
  67. package/dist/core/tokens/alias-resolver.d.ts.map +1 -0
  68. package/dist/core/tokens/alias-resolver.js +99 -0
  69. package/dist/core/tokens/alias-resolver.js.map +1 -0
  70. package/dist/core/tokens/config.d.ts +352 -0
  71. package/dist/core/tokens/config.d.ts.map +1 -0
  72. package/dist/core/tokens/config.js +285 -0
  73. package/dist/core/tokens/config.js.map +1 -0
  74. package/dist/core/tokens/figma-converter.d.ts +81 -0
  75. package/dist/core/tokens/figma-converter.d.ts.map +1 -0
  76. package/dist/core/tokens/figma-converter.js +196 -0
  77. package/dist/core/tokens/figma-converter.js.map +1 -0
  78. package/dist/core/tokens/formatters/css-vars.d.ts +24 -0
  79. package/dist/core/tokens/formatters/css-vars.d.ts.map +1 -0
  80. package/dist/core/tokens/formatters/css-vars.js +330 -0
  81. package/dist/core/tokens/formatters/css-vars.js.map +1 -0
  82. package/dist/core/tokens/formatters/dtcg.d.ts +28 -0
  83. package/dist/core/tokens/formatters/dtcg.d.ts.map +1 -0
  84. package/dist/core/tokens/formatters/dtcg.js +301 -0
  85. package/dist/core/tokens/formatters/dtcg.js.map +1 -0
  86. package/dist/core/tokens/formatters/index.d.ts +30 -0
  87. package/dist/core/tokens/formatters/index.d.ts.map +1 -0
  88. package/dist/core/tokens/formatters/index.js +46 -0
  89. package/dist/core/tokens/formatters/index.js.map +1 -0
  90. package/dist/core/tokens/formatters/json.d.ts +5 -0
  91. package/dist/core/tokens/formatters/json.d.ts.map +1 -0
  92. package/dist/core/tokens/formatters/json.js +8 -0
  93. package/dist/core/tokens/formatters/json.js.map +1 -0
  94. package/dist/core/tokens/formatters/less.d.ts +4 -0
  95. package/dist/core/tokens/formatters/less.d.ts.map +1 -0
  96. package/dist/core/tokens/formatters/less.js +5 -0
  97. package/dist/core/tokens/formatters/less.js.map +1 -0
  98. package/dist/core/tokens/formatters/scss.d.ts +4 -0
  99. package/dist/core/tokens/formatters/scss.d.ts.map +1 -0
  100. package/dist/core/tokens/formatters/scss.js +5 -0
  101. package/dist/core/tokens/formatters/scss.js.map +1 -0
  102. package/dist/core/tokens/formatters/stubs.d.ts +9 -0
  103. package/dist/core/tokens/formatters/stubs.d.ts.map +1 -0
  104. package/dist/core/tokens/formatters/stubs.js +12 -0
  105. package/dist/core/tokens/formatters/stubs.js.map +1 -0
  106. package/dist/core/tokens/formatters/style-dictionary-v3.d.ts +4 -0
  107. package/dist/core/tokens/formatters/style-dictionary-v3.d.ts.map +1 -0
  108. package/dist/core/tokens/formatters/style-dictionary-v3.js +5 -0
  109. package/dist/core/tokens/formatters/style-dictionary-v3.js.map +1 -0
  110. package/dist/core/tokens/formatters/tailwind-v3.d.ts +4 -0
  111. package/dist/core/tokens/formatters/tailwind-v3.d.ts.map +1 -0
  112. package/dist/core/tokens/formatters/tailwind-v3.js +5 -0
  113. package/dist/core/tokens/formatters/tailwind-v3.js.map +1 -0
  114. package/dist/core/tokens/formatters/tailwind-v4.d.ts +4 -0
  115. package/dist/core/tokens/formatters/tailwind-v4.d.ts.map +1 -0
  116. package/dist/core/tokens/formatters/tailwind-v4.js +5 -0
  117. package/dist/core/tokens/formatters/tailwind-v4.js.map +1 -0
  118. package/dist/core/tokens/formatters/tokens-studio.d.ts +4 -0
  119. package/dist/core/tokens/formatters/tokens-studio.d.ts.map +1 -0
  120. package/dist/core/tokens/formatters/tokens-studio.js +5 -0
  121. package/dist/core/tokens/formatters/tokens-studio.js.map +1 -0
  122. package/dist/core/tokens/formatters/ts-module.d.ts +4 -0
  123. package/dist/core/tokens/formatters/ts-module.d.ts.map +1 -0
  124. package/dist/core/tokens/formatters/ts-module.js +5 -0
  125. package/dist/core/tokens/formatters/ts-module.js.map +1 -0
  126. package/dist/core/tokens/index.d.ts +17 -0
  127. package/dist/core/tokens/index.d.ts.map +1 -0
  128. package/dist/core/tokens/index.js +16 -0
  129. package/dist/core/tokens/index.js.map +1 -0
  130. package/dist/core/tokens/parsers/css-vars.d.ts +3 -0
  131. package/dist/core/tokens/parsers/css-vars.d.ts.map +1 -0
  132. package/dist/core/tokens/parsers/css-vars.js +5 -0
  133. package/dist/core/tokens/parsers/css-vars.js.map +1 -0
  134. package/dist/core/tokens/parsers/dtcg.d.ts +21 -0
  135. package/dist/core/tokens/parsers/dtcg.d.ts.map +1 -0
  136. package/dist/core/tokens/parsers/dtcg.js +254 -0
  137. package/dist/core/tokens/parsers/dtcg.js.map +1 -0
  138. package/dist/core/tokens/parsers/index.d.ts +37 -0
  139. package/dist/core/tokens/parsers/index.d.ts.map +1 -0
  140. package/dist/core/tokens/parsers/index.js +139 -0
  141. package/dist/core/tokens/parsers/index.js.map +1 -0
  142. package/dist/core/tokens/parsers/json.d.ts +4 -0
  143. package/dist/core/tokens/parsers/json.d.ts.map +1 -0
  144. package/dist/core/tokens/parsers/json.js +8 -0
  145. package/dist/core/tokens/parsers/json.js.map +1 -0
  146. package/dist/core/tokens/parsers/scss.d.ts +3 -0
  147. package/dist/core/tokens/parsers/scss.d.ts.map +1 -0
  148. package/dist/core/tokens/parsers/scss.js +5 -0
  149. package/dist/core/tokens/parsers/scss.js.map +1 -0
  150. package/dist/core/tokens/parsers/stubs.d.ts +11 -0
  151. package/dist/core/tokens/parsers/stubs.d.ts.map +1 -0
  152. package/dist/core/tokens/parsers/stubs.js +14 -0
  153. package/dist/core/tokens/parsers/stubs.js.map +1 -0
  154. package/dist/core/tokens/parsers/style-dictionary-v3.d.ts +3 -0
  155. package/dist/core/tokens/parsers/style-dictionary-v3.d.ts.map +1 -0
  156. package/dist/core/tokens/parsers/style-dictionary-v3.js +5 -0
  157. package/dist/core/tokens/parsers/style-dictionary-v3.js.map +1 -0
  158. package/dist/core/tokens/parsers/tailwind-v3.d.ts +3 -0
  159. package/dist/core/tokens/parsers/tailwind-v3.d.ts.map +1 -0
  160. package/dist/core/tokens/parsers/tailwind-v3.js +5 -0
  161. package/dist/core/tokens/parsers/tailwind-v3.js.map +1 -0
  162. package/dist/core/tokens/parsers/tailwind-v4.d.ts +3 -0
  163. package/dist/core/tokens/parsers/tailwind-v4.d.ts.map +1 -0
  164. package/dist/core/tokens/parsers/tailwind-v4.js +5 -0
  165. package/dist/core/tokens/parsers/tailwind-v4.js.map +1 -0
  166. package/dist/core/tokens/parsers/tokens-studio.d.ts +3 -0
  167. package/dist/core/tokens/parsers/tokens-studio.d.ts.map +1 -0
  168. package/dist/core/tokens/parsers/tokens-studio.js +5 -0
  169. package/dist/core/tokens/parsers/tokens-studio.js.map +1 -0
  170. package/dist/core/tokens/schemas.d.ts +152 -0
  171. package/dist/core/tokens/schemas.d.ts.map +1 -0
  172. package/dist/core/tokens/schemas.js +149 -0
  173. package/dist/core/tokens/schemas.js.map +1 -0
  174. package/dist/core/tokens/transforms/color.d.ts +9 -0
  175. package/dist/core/tokens/transforms/color.d.ts.map +1 -0
  176. package/dist/core/tokens/transforms/color.js +13 -0
  177. package/dist/core/tokens/transforms/color.js.map +1 -0
  178. package/dist/core/tokens/transforms/index.d.ts +36 -0
  179. package/dist/core/tokens/transforms/index.d.ts.map +1 -0
  180. package/dist/core/tokens/transforms/index.js +30 -0
  181. package/dist/core/tokens/transforms/index.js.map +1 -0
  182. package/dist/core/tokens/transforms/size.d.ts +7 -0
  183. package/dist/core/tokens/transforms/size.d.ts.map +1 -0
  184. package/dist/core/tokens/transforms/size.js +8 -0
  185. package/dist/core/tokens/transforms/size.js.map +1 -0
  186. package/dist/core/tokens/types.d.ts +228 -0
  187. package/dist/core/tokens/types.d.ts.map +1 -0
  188. package/dist/core/tokens/types.js +19 -0
  189. package/dist/core/tokens/types.js.map +1 -0
  190. package/dist/core/tokens-tools.d.ts +42 -0
  191. package/dist/core/tokens-tools.d.ts.map +1 -0
  192. package/dist/core/tokens-tools.js +850 -0
  193. package/dist/core/tokens-tools.js.map +1 -0
  194. package/dist/core/types/index.d.ts +0 -8
  195. package/dist/core/types/index.d.ts.map +1 -1
  196. package/dist/core/version-tools.d.ts +30 -1
  197. package/dist/core/version-tools.d.ts.map +1 -1
  198. package/dist/core/version-tools.js +151 -7
  199. package/dist/core/version-tools.js.map +1 -1
  200. package/dist/core/websocket-connector.d.ts +1 -1
  201. package/dist/core/websocket-connector.d.ts.map +1 -1
  202. package/dist/core/websocket-server.d.ts +47 -3
  203. package/dist/core/websocket-server.d.ts.map +1 -1
  204. package/dist/core/websocket-server.js +77 -55
  205. package/dist/core/websocket-server.js.map +1 -1
  206. package/dist/local.d.ts +0 -12
  207. package/dist/local.d.ts.map +1 -1
  208. package/dist/local.js +967 -3406
  209. package/dist/local.js.map +1 -1
  210. package/figma-desktop-bridge/code.js +59 -63
  211. package/figma-desktop-bridge/ui.html +85 -11
  212. package/package.json +12 -30
  213. 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 window
3
- // Supports: Variables, Components, Styles, and more
4
- // Uses postMessage to communicate with UI, bypassing worker sandbox limitations
5
- // Puppeteer can access UI iframe's window context to retrieve data
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.23.0'; // Kept in sync with package.json by scripts/release.sh — see issue #62.
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: 140, height: 50, visible: true, themeColors: true });
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 enables console monitoring without CDP.
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 show up properly in Puppeteer
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: 140, height: 50, visible: true, themeColors: true });
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 for Puppeteer to read data from window object
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 - Data storage for Puppeteer/MCP access
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
- // UI update helper
231
- function updateStatus(state, isActive, isError) {
232
- const dot = document.getElementById('status-dot');
233
- const stateText = document.getElementById('status-state');
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
- dot.className = 'status-indicator ' + (isError ? 'error' : (isActive ? 'active' : 'loading'));
236
- stateText.textContent = state;
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 - Fallback transport when CDP is unavailable
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: 140, height: height } }, '*');
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.23.1",
4
- "description": "MCP server for accessing Figma plugin console logs and screenshots via Cloudflare Workers or local mode",
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
- "console",
41
- "debugging",
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": "Your Name",
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
- "puppeteer-core": "^23.11.1",
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": ">=6.4.2",
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
  }