@atezer/figma-mcp-bridge 1.1.1 → 1.1.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/LICENSE +11 -0
- package/README.md +153 -117
- package/dist/cloudflare/core/figma-desktop-connector.js +28 -16
- package/dist/cloudflare/core/plugin-bridge-connector.js +38 -4
- package/dist/cloudflare/core/plugin-bridge-server.js +42 -13
- package/dist/core/figma-desktop-connector.d.ts +6 -2
- package/dist/core/figma-desktop-connector.d.ts.map +1 -1
- package/dist/core/figma-desktop-connector.js +28 -16
- package/dist/core/figma-desktop-connector.js.map +1 -1
- package/dist/core/plugin-bridge-connector.d.ts +18 -2
- package/dist/core/plugin-bridge-connector.d.ts.map +1 -1
- package/dist/core/plugin-bridge-connector.js +38 -4
- package/dist/core/plugin-bridge-connector.js.map +1 -1
- package/dist/core/plugin-bridge-server.d.ts +2 -1
- package/dist/core/plugin-bridge-server.d.ts.map +1 -1
- package/dist/core/plugin-bridge-server.js +42 -13
- 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 +69 -7
- package/dist/local-plugin-only.js.map +1 -1
- package/f-mcp-plugin/code.js +314 -53
- package/f-mcp-plugin/manifest.json +21 -2
- package/f-mcp-plugin/ui.html +135 -23
- package/package.json +3 -2
package/f-mcp-plugin/ui.html
CHANGED
|
@@ -9,12 +9,27 @@
|
|
|
9
9
|
padding: 0;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
html, body {
|
|
13
|
+
overflow: hidden !important;
|
|
14
|
+
overflow-x: hidden !important;
|
|
15
|
+
overflow-y: hidden !important;
|
|
16
|
+
/* Scrollbar'ları gizle; içerik ne ise öyle görünsün (taşma yok) */
|
|
17
|
+
-ms-overflow-style: none;
|
|
18
|
+
scrollbar-width: none;
|
|
19
|
+
}
|
|
20
|
+
html::-webkit-scrollbar, body::-webkit-scrollbar {
|
|
21
|
+
display: none;
|
|
22
|
+
}
|
|
23
|
+
|
|
12
24
|
body {
|
|
13
25
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
14
26
|
font-size: 11px;
|
|
15
27
|
background: var(--figma-color-bg, #2c2c2c);
|
|
16
28
|
color: var(--figma-color-text, rgba(255, 255, 255, 0.9));
|
|
17
|
-
|
|
29
|
+
width: fit-content;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
height: auto;
|
|
32
|
+
min-height: 0;
|
|
18
33
|
display: flex;
|
|
19
34
|
align-items: center;
|
|
20
35
|
justify-content: center;
|
|
@@ -83,17 +98,29 @@
|
|
|
83
98
|
display: flex;
|
|
84
99
|
flex-direction: column;
|
|
85
100
|
gap: 6px;
|
|
86
|
-
width:
|
|
101
|
+
width: fit-content;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
max-width: 100%;
|
|
104
|
+
overflow: hidden;
|
|
87
105
|
}
|
|
88
|
-
.bridge-port {
|
|
106
|
+
.bridge-host, .bridge-port {
|
|
89
107
|
display: flex;
|
|
90
108
|
align-items: center;
|
|
91
109
|
gap: 6px;
|
|
92
110
|
font-size: 10px;
|
|
93
111
|
}
|
|
94
|
-
.bridge-port label {
|
|
112
|
+
.bridge-host label, .bridge-port label {
|
|
95
113
|
color: var(--figma-color-text-secondary, rgba(255, 255, 255, 0.7));
|
|
96
114
|
}
|
|
115
|
+
.bridge-host input {
|
|
116
|
+
width: 90px;
|
|
117
|
+
padding: 2px 4px;
|
|
118
|
+
background: var(--figma-color-bg-tertiary, #1e1e1e);
|
|
119
|
+
border: 1px solid var(--figma-color-border, #4a4a4a);
|
|
120
|
+
border-radius: 3px;
|
|
121
|
+
color: inherit;
|
|
122
|
+
font-size: 11px;
|
|
123
|
+
}
|
|
97
124
|
.bridge-port input {
|
|
98
125
|
width: 56px;
|
|
99
126
|
padding: 2px 4px;
|
|
@@ -125,16 +152,20 @@
|
|
|
125
152
|
</head>
|
|
126
153
|
<body>
|
|
127
154
|
<div class="bridge-row">
|
|
128
|
-
<div class="bridge-status" id="status-container" title="
|
|
155
|
+
<div class="bridge-status" id="status-container" title="'no server': MCP bridge çalıştığından emin olun. Browser Figma'da da çalışır (aynı makinede localhost, uzakta IP girin).">
|
|
129
156
|
<div class="status-indicator loading" id="status-dot"></div>
|
|
130
157
|
<div class="status-text">
|
|
131
158
|
<span class="label">MCP</span>
|
|
132
159
|
<span class="state" id="status-state">connecting</span>
|
|
133
160
|
</div>
|
|
134
161
|
</div>
|
|
135
|
-
<div class="bridge-
|
|
162
|
+
<div class="bridge-host" title="Host: localhost (aynı makine) veya IP adresi (uzak makine). Browser Figma'da da çalışır.">
|
|
163
|
+
<label for="mcp-host">Host</label>
|
|
164
|
+
<input type="text" id="mcp-host" value="localhost" placeholder="localhost" aria-label="MCP bridge host (localhost veya IP)" />
|
|
165
|
+
</div>
|
|
166
|
+
<div class="bridge-port" title="Port: Claude açıkken 5454'te bağlanır. Çoklu kullanıcıda her kullanıcı farklı port (5455–5470) seçer.">
|
|
136
167
|
<label for="mcp-port">Port</label>
|
|
137
|
-
<input type="number" id="mcp-port" min="5454" max="5470" value="5454" />
|
|
168
|
+
<input type="number" id="mcp-port" min="5454" max="5470" value="5454" aria-label="MCP bridge port (5454 varsayılan, Claude açıkken bağlanır)" />
|
|
138
169
|
</div>
|
|
139
170
|
</div>
|
|
140
171
|
|
|
@@ -257,8 +288,11 @@
|
|
|
257
288
|
// COMPONENT OPERATIONS
|
|
258
289
|
// ============================================================================
|
|
259
290
|
|
|
260
|
-
window.getLocalComponents = () => {
|
|
261
|
-
|
|
291
|
+
window.getLocalComponents = (currentPageOnly, limit) => {
|
|
292
|
+
var params = {};
|
|
293
|
+
params.currentPageOnly = currentPageOnly !== false;
|
|
294
|
+
if (limit != null && limit > 0) params.limit = limit;
|
|
295
|
+
return window.sendPluginCommand('GET_LOCAL_COMPONENTS', params, 120000)
|
|
262
296
|
.catch(function(err) { return { success: false, error: err.message || String(err) }; });
|
|
263
297
|
};
|
|
264
298
|
|
|
@@ -588,6 +622,9 @@
|
|
|
588
622
|
case 'GET_DOCUMENT_STRUCTURE_RESULT':
|
|
589
623
|
handleResult('GET_DOCUMENT_STRUCTURE', null);
|
|
590
624
|
break;
|
|
625
|
+
case 'GET_NODE_CONTEXT_RESULT':
|
|
626
|
+
handleResult('GET_NODE_CONTEXT', null);
|
|
627
|
+
break;
|
|
591
628
|
case 'GET_LOCAL_STYLES_RESULT':
|
|
592
629
|
handleResult('GET_LOCAL_STYLES', null);
|
|
593
630
|
break;
|
|
@@ -612,8 +649,26 @@
|
|
|
612
649
|
}
|
|
613
650
|
};
|
|
614
651
|
|
|
615
|
-
window.getDocumentStructure = (depth, verbosity) => {
|
|
616
|
-
|
|
652
|
+
window.getDocumentStructure = (depth, verbosity, includeLayout, includeVisual, includeTypography, includeCodeReady, outputHint) => {
|
|
653
|
+
var payload = { depth: depth || 1, verbosity: verbosity || 'summary' };
|
|
654
|
+
if (includeLayout !== undefined) payload.includeLayout = !!includeLayout;
|
|
655
|
+
if (includeVisual !== undefined) payload.includeVisual = !!includeVisual;
|
|
656
|
+
if (includeTypography !== undefined) payload.includeTypography = !!includeTypography;
|
|
657
|
+
if (includeCodeReady !== undefined) payload.includeCodeReady = !!includeCodeReady;
|
|
658
|
+
if (outputHint !== undefined && outputHint != null) payload.outputHint = outputHint;
|
|
659
|
+
return window.sendPluginCommand('GET_DOCUMENT_STRUCTURE', payload, 90000).then(function(r) {
|
|
660
|
+
if (r && r.success === false) return r;
|
|
661
|
+
return r && r.data !== undefined ? r.data : r;
|
|
662
|
+
});
|
|
663
|
+
};
|
|
664
|
+
window.getNodeContext = (nodeId, depth, verbosity, includeLayout, includeVisual, includeTypography, includeCodeReady, outputHint) => {
|
|
665
|
+
var payload = { nodeId: nodeId, depth: depth || 2, verbosity: verbosity || 'standard' };
|
|
666
|
+
if (includeLayout !== undefined) payload.includeLayout = !!includeLayout;
|
|
667
|
+
if (includeVisual !== undefined) payload.includeVisual = !!includeVisual;
|
|
668
|
+
if (includeTypography !== undefined) payload.includeTypography = !!includeTypography;
|
|
669
|
+
if (includeCodeReady !== undefined) payload.includeCodeReady = !!includeCodeReady;
|
|
670
|
+
if (outputHint !== undefined && outputHint != null) payload.outputHint = outputHint;
|
|
671
|
+
return window.sendPluginCommand('GET_NODE_CONTEXT', payload, 90000).then(function(r) {
|
|
617
672
|
if (r && r.success === false) return r;
|
|
618
673
|
return r && r.data !== undefined ? r.data : r;
|
|
619
674
|
});
|
|
@@ -649,12 +704,20 @@
|
|
|
649
704
|
|
|
650
705
|
// ============================================================================
|
|
651
706
|
// MCP PLUGIN BRIDGE - WebSocket client (no Figma debug port needed)
|
|
652
|
-
// Port configurable
|
|
707
|
+
// Host + Port configurable: browser Figma ve uzak makine destegi
|
|
653
708
|
// ============================================================================
|
|
654
|
-
var
|
|
709
|
+
var MCP_BRIDGE_PORT_KEY = 'f-mcp-bridge-port';
|
|
710
|
+
var MCP_BRIDGE_HOST_KEY = 'f-mcp-bridge-host';
|
|
655
711
|
var mcpBridgeWs = null;
|
|
656
712
|
var mcpBridgeReconnectTimer = null;
|
|
657
713
|
|
|
714
|
+
function getMcpBridgeHost() {
|
|
715
|
+
var el = document.getElementById('mcp-host');
|
|
716
|
+
if (!el) return 'localhost';
|
|
717
|
+
var v = (el.value || '').trim();
|
|
718
|
+
return v || 'localhost';
|
|
719
|
+
}
|
|
720
|
+
|
|
658
721
|
function getMcpBridgePort() {
|
|
659
722
|
var el = document.getElementById('mcp-port');
|
|
660
723
|
if (!el) return 5454;
|
|
@@ -663,9 +726,31 @@
|
|
|
663
726
|
return n;
|
|
664
727
|
}
|
|
665
728
|
|
|
729
|
+
function initMcpHostInput() {
|
|
730
|
+
try {
|
|
731
|
+
var stored = localStorage.getItem(MCP_BRIDGE_HOST_KEY);
|
|
732
|
+
if (stored) {
|
|
733
|
+
var el = document.getElementById('mcp-host');
|
|
734
|
+
if (el) { el.value = stored; }
|
|
735
|
+
}
|
|
736
|
+
} catch (e) {}
|
|
737
|
+
var el = document.getElementById('mcp-host');
|
|
738
|
+
if (el) {
|
|
739
|
+
el.addEventListener('change', function() {
|
|
740
|
+
var host = getMcpBridgeHost();
|
|
741
|
+
try { localStorage.setItem(MCP_BRIDGE_HOST_KEY, host); } catch (e) {}
|
|
742
|
+
if (mcpBridgeWs) {
|
|
743
|
+
mcpBridgeWs.close();
|
|
744
|
+
mcpBridgeWs = null;
|
|
745
|
+
}
|
|
746
|
+
connectMcpBridge();
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
666
751
|
function initMcpPortInput() {
|
|
667
752
|
try {
|
|
668
|
-
var stored = localStorage.getItem(
|
|
753
|
+
var stored = localStorage.getItem(MCP_BRIDGE_PORT_KEY);
|
|
669
754
|
if (stored) {
|
|
670
755
|
var n = parseInt(stored, 10);
|
|
671
756
|
if (n >= 5454 && n <= 5470) {
|
|
@@ -678,7 +763,7 @@
|
|
|
678
763
|
if (el) {
|
|
679
764
|
el.addEventListener('change', function() {
|
|
680
765
|
var port = getMcpBridgePort();
|
|
681
|
-
try { localStorage.setItem(
|
|
766
|
+
try { localStorage.setItem(MCP_BRIDGE_PORT_KEY, String(port)); } catch (e) {}
|
|
682
767
|
if (mcpBridgeWs) {
|
|
683
768
|
mcpBridgeWs.close();
|
|
684
769
|
mcpBridgeWs = null;
|
|
@@ -688,29 +773,52 @@
|
|
|
688
773
|
}
|
|
689
774
|
}
|
|
690
775
|
|
|
776
|
+
var mcpHandshakeTimer = null;
|
|
777
|
+
var mcpHandshakeDone = false;
|
|
778
|
+
|
|
691
779
|
function connectMcpBridge() {
|
|
692
780
|
if (mcpBridgeWs && mcpBridgeWs.readyState === 1) return;
|
|
781
|
+
var host = getMcpBridgeHost();
|
|
693
782
|
var port = getMcpBridgePort();
|
|
694
|
-
var url = 'ws://
|
|
783
|
+
var url = 'ws://' + host + ':' + port;
|
|
784
|
+
mcpHandshakeDone = false;
|
|
695
785
|
try {
|
|
696
786
|
mcpBridgeWs = new WebSocket(url);
|
|
697
787
|
mcpBridgeWs.onopen = function() {
|
|
698
|
-
console.log('[F-MCP ATezer Bridge]
|
|
699
|
-
updateStatus('
|
|
788
|
+
console.log('[F-MCP ATezer Bridge] WebSocket opened to ' + url + ', waiting for handshake...');
|
|
789
|
+
updateStatus('connecting...', false, false);
|
|
700
790
|
mcpBridgeWs.send(JSON.stringify({ type: 'ready' }));
|
|
791
|
+
if (mcpHandshakeTimer) clearTimeout(mcpHandshakeTimer);
|
|
792
|
+
mcpHandshakeTimer = setTimeout(function() {
|
|
793
|
+
if (!mcpHandshakeDone && mcpBridgeWs) {
|
|
794
|
+
console.warn('[F-MCP ATezer Bridge] No welcome from server — wrong server or outdated bridge');
|
|
795
|
+
updateStatus('wrong server', false, true);
|
|
796
|
+
}
|
|
797
|
+
}, 5000);
|
|
701
798
|
};
|
|
702
799
|
mcpBridgeWs.onmessage = function(event) {
|
|
703
800
|
try {
|
|
704
801
|
var msg = JSON.parse(event.data);
|
|
802
|
+
if (msg.type === 'welcome') {
|
|
803
|
+
mcpHandshakeDone = true;
|
|
804
|
+
if (mcpHandshakeTimer) { clearTimeout(mcpHandshakeTimer); mcpHandshakeTimer = null; }
|
|
805
|
+
var connectedPort = msg.port || port;
|
|
806
|
+
console.log('[F-MCP ATezer Bridge] Handshake OK — bridge v' + (msg.bridgeVersion || '?') + ' on port ' + connectedPort);
|
|
807
|
+
updateStatus('ready (:' + connectedPort + ')', true, false);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
705
810
|
if (!msg.id || !msg.method) return;
|
|
706
811
|
var id = msg.id;
|
|
707
812
|
var method = msg.method;
|
|
708
813
|
var params = msg.params || {};
|
|
709
814
|
var run = function() {
|
|
710
815
|
if (method === 'getVariablesFromPluginUI') {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
816
|
+
// Always refresh first (async API) so variables work in dynamic-page context
|
|
817
|
+
return window.refreshVariables().then(function(r) {
|
|
818
|
+
if (r && r.success === false) return { success: false, error: r.error || 'Variables refresh failed' };
|
|
819
|
+
if (!window.__figmaVariablesData) return { success: false, error: 'Variables not loaded' };
|
|
820
|
+
return { success: true, variables: window.__figmaVariablesData.variables, variableCollections: window.__figmaVariablesData.variableCollections };
|
|
821
|
+
});
|
|
714
822
|
}
|
|
715
823
|
if (method === 'getComponentFromPluginUI') {
|
|
716
824
|
return window.requestComponentData(params.nodeId).then(function(data) {
|
|
@@ -758,7 +866,7 @@
|
|
|
758
866
|
return window.refreshVariables();
|
|
759
867
|
}
|
|
760
868
|
if (method === 'getLocalComponents') {
|
|
761
|
-
return window.getLocalComponents();
|
|
869
|
+
return window.getLocalComponents(params.currentPageOnly, params.limit);
|
|
762
870
|
}
|
|
763
871
|
if (method === 'instantiateComponent') {
|
|
764
872
|
return window.instantiateComponent(params.componentKey, params.options);
|
|
@@ -815,7 +923,10 @@
|
|
|
815
923
|
return window.setInstanceProperties(params.nodeId, params.properties);
|
|
816
924
|
}
|
|
817
925
|
if (method === 'getDocumentStructure') {
|
|
818
|
-
return window.getDocumentStructure(params.depth, params.verbosity);
|
|
926
|
+
return window.getDocumentStructure(params.depth, params.verbosity, params.includeLayout, params.includeVisual, params.includeTypography, params.includeCodeReady, params.outputHint);
|
|
927
|
+
}
|
|
928
|
+
if (method === 'getNodeContext') {
|
|
929
|
+
return window.getNodeContext(params.nodeId, params.depth, params.verbosity, params.includeLayout, params.includeVisual, params.includeTypography, params.includeCodeReady, params.outputHint);
|
|
819
930
|
}
|
|
820
931
|
if (method === 'getLocalStyles') {
|
|
821
932
|
return window.getLocalStyles(params.verbosity);
|
|
@@ -869,6 +980,7 @@
|
|
|
869
980
|
}
|
|
870
981
|
}
|
|
871
982
|
if (typeof WebSocket !== 'undefined') {
|
|
983
|
+
initMcpHostInput();
|
|
872
984
|
initMcpPortInput();
|
|
873
985
|
connectMcpBridge();
|
|
874
986
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atezer/figma-mcp-bridge",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "F-MCP ATezer: MCP server and Figma plugin bridge for Claude/Cursor. No REST token required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/local.js",
|
|
7
7
|
"types": "dist/local.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"figma-mcp-bridge": "./dist/local.js"
|
|
9
|
+
"figma-mcp-bridge": "./dist/local-plugin-only.js",
|
|
10
|
+
"figma-mcp-bridge-full": "./dist/local.js"
|
|
10
11
|
},
|
|
11
12
|
"files": [
|
|
12
13
|
"dist",
|