@atezer/figma-mcp-bridge 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -9
- package/README.md +8 -10
- package/dist/cloudflare/index.js +4 -243
- package/dist/core/plugin-bridge-server.d.ts +16 -0
- package/dist/core/plugin-bridge-server.d.ts.map +1 -1
- package/dist/core/plugin-bridge-server.js +49 -12
- 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 -6
- package/dist/local-plugin-only.js.map +1 -1
- package/f-mcp-plugin/README.md +0 -8
- package/f-mcp-plugin/manifest.json +2 -4
- package/f-mcp-plugin/ui.html +193 -450
- package/package.json +7 -3
- package/dist/cloudflare/cloud-cors.js +0 -40
- package/dist/cloudflare/cloud-mode-kv.js +0 -86
- package/dist/cloudflare/cloud-mode-routes.js +0 -97
- package/dist/cloudflare/cloud-relay-session.js +0 -141
package/f-mcp-plugin/ui.html
CHANGED
|
@@ -172,62 +172,6 @@
|
|
|
172
172
|
opacity: 0.9;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
.cloud-panel {
|
|
176
|
-
display: flex;
|
|
177
|
-
flex-direction: column;
|
|
178
|
-
gap: 6px;
|
|
179
|
-
padding: 8px 10px;
|
|
180
|
-
background: var(--figma-color-bg-secondary, #383838);
|
|
181
|
-
border: 1px solid var(--figma-color-border, #4a4a4a);
|
|
182
|
-
border-radius: 8px;
|
|
183
|
-
font-size: 10px;
|
|
184
|
-
}
|
|
185
|
-
.cloud-panel label.cloud-toggle {
|
|
186
|
-
display: flex;
|
|
187
|
-
align-items: center;
|
|
188
|
-
gap: 6px;
|
|
189
|
-
cursor: pointer;
|
|
190
|
-
font-weight: 500;
|
|
191
|
-
}
|
|
192
|
-
.cloud-fields {
|
|
193
|
-
display: none;
|
|
194
|
-
flex-direction: column;
|
|
195
|
-
gap: 6px;
|
|
196
|
-
}
|
|
197
|
-
.cloud-fields.is-visible {
|
|
198
|
-
display: flex;
|
|
199
|
-
}
|
|
200
|
-
.cloud-fields .field label {
|
|
201
|
-
display: block;
|
|
202
|
-
color: var(--figma-color-text-secondary, rgba(255, 255, 255, 0.7));
|
|
203
|
-
margin-bottom: 2px;
|
|
204
|
-
}
|
|
205
|
-
.cloud-fields input[type="text"],
|
|
206
|
-
.cloud-fields input[type="password"] {
|
|
207
|
-
width: 100%;
|
|
208
|
-
max-width: 220px;
|
|
209
|
-
padding: 4px 6px;
|
|
210
|
-
background: var(--figma-color-bg-tertiary, #1e1e1e);
|
|
211
|
-
border: 1px solid var(--figma-color-border, #4a4a4a);
|
|
212
|
-
border-radius: 4px;
|
|
213
|
-
color: inherit;
|
|
214
|
-
font-size: 11px;
|
|
215
|
-
}
|
|
216
|
-
.cloud-connect {
|
|
217
|
-
align-self: flex-start;
|
|
218
|
-
border: 1px solid var(--figma-color-border, #4a4a4a);
|
|
219
|
-
background: transparent;
|
|
220
|
-
color: inherit;
|
|
221
|
-
border-radius: 6px;
|
|
222
|
-
font-size: 10px;
|
|
223
|
-
padding: 4px 10px;
|
|
224
|
-
cursor: pointer;
|
|
225
|
-
}
|
|
226
|
-
.cloud-hint {
|
|
227
|
-
font-size: 9px;
|
|
228
|
-
color: var(--figma-color-text-secondary, rgba(255, 255, 255, 0.55));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
175
|
/* Light theme support */
|
|
232
176
|
@media (prefers-color-scheme: light) {
|
|
233
177
|
body {
|
|
@@ -275,25 +219,6 @@
|
|
|
275
219
|
</div>
|
|
276
220
|
<button type="button" id="auto-port-reset" class="auto-port-reset" title="Tek porta kilitlenmeyi kaldırır; 5454–5470 yeniden taranır (Claude config portu ile eşleşmeli)." aria-label="5454 ile 5470 arası otomatik port taraması">Otomatik tara</button>
|
|
277
221
|
</div>
|
|
278
|
-
<div class="cloud-panel" id="cloud-panel">
|
|
279
|
-
<label class="cloud-toggle"><input type="checkbox" id="cloud-mode-toggle" aria-label="Cloud Mode" /> Cloud Mode (uzak MCP, yerel Node yok)</label>
|
|
280
|
-
<div class="cloud-fields" id="cloud-fields">
|
|
281
|
-
<div class="field">
|
|
282
|
-
<label for="cloud-base">Worker URL (https://…)</label>
|
|
283
|
-
<input type="text" id="cloud-base" placeholder="https://figma-mcp-bridge.workers.dev" autocomplete="off" />
|
|
284
|
-
</div>
|
|
285
|
-
<div class="field">
|
|
286
|
-
<label for="cloud-code">Pairing code</label>
|
|
287
|
-
<input type="text" id="cloud-code" maxlength="8" placeholder="6 karakter" autocomplete="off" />
|
|
288
|
-
</div>
|
|
289
|
-
<div class="field">
|
|
290
|
-
<label for="cloud-secret">Secret</label>
|
|
291
|
-
<input type="password" id="cloud-secret" placeholder="AI / fmcp_generate_pairing_code çıktısı" autocomplete="off" />
|
|
292
|
-
</div>
|
|
293
|
-
<button type="button" id="cloud-connect" class="cloud-connect">Bağlan</button>
|
|
294
|
-
<div class="cloud-hint" id="cloud-hint">Kodlar yaklaşık 5 dk geçerlidir. Önce uzak MCP’de fmcp_generate_pairing_code çalıştırın, sonra buraya yapıştırın.</div>
|
|
295
|
-
</div>
|
|
296
|
-
</div>
|
|
297
222
|
</div>
|
|
298
223
|
|
|
299
224
|
<script>
|
|
@@ -670,7 +595,6 @@
|
|
|
670
595
|
window.__figmaFileKey = msg.fileKey || null;
|
|
671
596
|
window.__figmaFileName = msg.fileName || null;
|
|
672
597
|
console.log('[F-MCP ATezer Bridge] File identity: ' + (msg.fileName || '?') + ' (' + (msg.fileKey || '?') + ')');
|
|
673
|
-
pushBridgeFileIdentity();
|
|
674
598
|
break;
|
|
675
599
|
|
|
676
600
|
case 'VARIABLES_DATA':
|
|
@@ -906,71 +830,6 @@
|
|
|
906
830
|
var candidatePorts = [];
|
|
907
831
|
var candidatePortIndex = 0;
|
|
908
832
|
|
|
909
|
-
/**
|
|
910
|
-
* WebSocket onopen often runs before FILE_IDENTITY arrives from the plugin main thread.
|
|
911
|
-
* Re-send "ready" when fileKey/fileName are known so the bridge can route multi-client RPCs.
|
|
912
|
-
*/
|
|
913
|
-
function pushBridgeFileIdentity() {
|
|
914
|
-
if (!mcpBridgeWs || mcpBridgeWs.readyState !== 1) return;
|
|
915
|
-
try {
|
|
916
|
-
mcpBridgeWs.send(JSON.stringify({
|
|
917
|
-
type: 'ready',
|
|
918
|
-
fileKey: window.__figmaFileKey || null,
|
|
919
|
-
fileName: window.__figmaFileName || null
|
|
920
|
-
}));
|
|
921
|
-
} catch (_) {}
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
var CLOUD_MODE_KEY = 'f-mcp-cloud-mode';
|
|
925
|
-
var CLOUD_BASE_STORAGE = 'f-mcp-cloud-base';
|
|
926
|
-
|
|
927
|
-
function isCloudMode() {
|
|
928
|
-
var el = document.getElementById('cloud-mode-toggle');
|
|
929
|
-
return !!(el && el.checked);
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
function syncCloudFieldsVisibility() {
|
|
933
|
-
var f = document.getElementById('cloud-fields');
|
|
934
|
-
if (f) f.classList.toggle('is-visible', isCloudMode());
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
function buildCloudWsUrl() {
|
|
938
|
-
var baseEl = document.getElementById('cloud-base');
|
|
939
|
-
var codeEl = document.getElementById('cloud-code');
|
|
940
|
-
var secEl = document.getElementById('cloud-secret');
|
|
941
|
-
var base = (baseEl && baseEl.value || '').trim().replace(/\/$/, '');
|
|
942
|
-
var code = (codeEl && codeEl.value || '').trim().toUpperCase();
|
|
943
|
-
var secret = (secEl && secEl.value || '').trim();
|
|
944
|
-
if (!base || !code || !secret) return null;
|
|
945
|
-
var wsBase = base.indexOf('https://') === 0 ? 'wss://' + base.slice(8) : (base.indexOf('http://') === 0 ? 'ws://' + base.slice(7) : base);
|
|
946
|
-
if (wsBase.indexOf('wss://') !== 0 && wsBase.indexOf('ws://') !== 0) wsBase = 'wss://' + wsBase;
|
|
947
|
-
return wsBase + '/fmcp-cloud/plugin?code=' + encodeURIComponent(code) + '&secret=' + encodeURIComponent(secret);
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
function initCloudModeUi() {
|
|
951
|
-
var toggle = document.getElementById('cloud-mode-toggle');
|
|
952
|
-
var baseEl = document.getElementById('cloud-base');
|
|
953
|
-
try {
|
|
954
|
-
if (baseEl && localStorage.getItem(CLOUD_BASE_STORAGE)) baseEl.value = localStorage.getItem(CLOUD_BASE_STORAGE);
|
|
955
|
-
if (toggle && localStorage.getItem(CLOUD_MODE_KEY) === '1') toggle.checked = true;
|
|
956
|
-
} catch (e) {}
|
|
957
|
-
syncCloudFieldsVisibility();
|
|
958
|
-
if (toggle) {
|
|
959
|
-
toggle.addEventListener('change', function() {
|
|
960
|
-
try { localStorage.setItem(CLOUD_MODE_KEY, toggle.checked ? '1' : '0'); } catch (e2) {}
|
|
961
|
-
syncCloudFieldsVisibility();
|
|
962
|
-
forceDisconnectAndReconnect();
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
if (baseEl) {
|
|
966
|
-
baseEl.addEventListener('change', function() {
|
|
967
|
-
try { localStorage.setItem(CLOUD_BASE_STORAGE, (baseEl.value || '').trim()); } catch (e3) {}
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
var cbtn = document.getElementById('cloud-connect');
|
|
971
|
-
if (cbtn) cbtn.addEventListener('click', function() { forceDisconnectAndReconnect(); });
|
|
972
|
-
}
|
|
973
|
-
|
|
974
833
|
/** Port alanı elle değiştirilince manualPortLocked=true olur; bu kilit 5454–5470 taramasını durdurur. */
|
|
975
834
|
function unlockAutoPortScan() {
|
|
976
835
|
manualPortLocked = false;
|
|
@@ -1058,8 +917,6 @@
|
|
|
1058
917
|
}
|
|
1059
918
|
|
|
1060
919
|
function forceDisconnectAndReconnect() {
|
|
1061
|
-
stopCloudPairCountdown();
|
|
1062
|
-
resetCloudHintDefault();
|
|
1063
920
|
if (mcpBridgeReconnectTimer) { clearTimeout(mcpBridgeReconnectTimer); mcpBridgeReconnectTimer = null; }
|
|
1064
921
|
mcpReconnectDelay = MCP_RECONNECT_MIN;
|
|
1065
922
|
candidatePorts = listCandidatePorts();
|
|
@@ -1121,46 +978,6 @@
|
|
|
1121
978
|
var mcpHandshakeTimer = null;
|
|
1122
979
|
var mcpHandshakeDone = false;
|
|
1123
980
|
var mcpKeepAliveTimer = null;
|
|
1124
|
-
var cloudPairCountdownTimer = null;
|
|
1125
|
-
var CLOUD_PAIR_TTL_SEC = 300;
|
|
1126
|
-
var CLOUD_HINT_DEFAULT = 'Kodlar yaklaşık 5 dk geçerlidir. Önce uzak MCP’de fmcp_generate_pairing_code çalıştırın, sonra buraya yapıştırın.';
|
|
1127
|
-
|
|
1128
|
-
function stopCloudPairCountdown() {
|
|
1129
|
-
if (cloudPairCountdownTimer) {
|
|
1130
|
-
clearInterval(cloudPairCountdownTimer);
|
|
1131
|
-
cloudPairCountdownTimer = null;
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
function startCloudPairCountdown() {
|
|
1136
|
-
stopCloudPairCountdown();
|
|
1137
|
-
var hint = document.getElementById('cloud-hint');
|
|
1138
|
-
var left = CLOUD_PAIR_TTL_SEC;
|
|
1139
|
-
function fmt(t) {
|
|
1140
|
-
var m = Math.floor(t / 60);
|
|
1141
|
-
var s = t % 60;
|
|
1142
|
-
return m + ':' + (s < 10 ? '0' : '') + s;
|
|
1143
|
-
}
|
|
1144
|
-
function tick() {
|
|
1145
|
-
if (hint) {
|
|
1146
|
-
hint.textContent = left > 0
|
|
1147
|
-
? ('Pairing penceresi (yakl.): ' + fmt(left) + ' — sunucu TTL 5 dk')
|
|
1148
|
-
: 'Süre doldu; uzak MCP’de fmcp_generate_pairing_code ile yeni kod alın.';
|
|
1149
|
-
}
|
|
1150
|
-
if (left <= 0) {
|
|
1151
|
-
stopCloudPairCountdown();
|
|
1152
|
-
return;
|
|
1153
|
-
}
|
|
1154
|
-
left--;
|
|
1155
|
-
}
|
|
1156
|
-
tick();
|
|
1157
|
-
cloudPairCountdownTimer = setInterval(tick, 1000);
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
function resetCloudHintDefault() {
|
|
1161
|
-
var hint = document.getElementById('cloud-hint');
|
|
1162
|
-
if (hint) hint.textContent = CLOUD_HINT_DEFAULT;
|
|
1163
|
-
}
|
|
1164
981
|
|
|
1165
982
|
function scheduleReconnect() {
|
|
1166
983
|
if (mcpBridgeReconnectTimer) clearTimeout(mcpBridgeReconnectTimer);
|
|
@@ -1187,269 +1004,7 @@
|
|
|
1187
1004
|
if (mcpKeepAliveTimer) { clearInterval(mcpKeepAliveTimer); mcpKeepAliveTimer = null; }
|
|
1188
1005
|
}
|
|
1189
1006
|
|
|
1190
|
-
function handleMcpBridgeMessage(event) {
|
|
1191
|
-
try {
|
|
1192
|
-
var msg = JSON.parse(event.data);
|
|
1193
|
-
if (msg.type === 'welcome') {
|
|
1194
|
-
mcpHandshakeDone = true;
|
|
1195
|
-
if (mcpHandshakeTimer) { clearTimeout(mcpHandshakeTimer); mcpHandshakeTimer = null; }
|
|
1196
|
-
if (isCloudMode()) {
|
|
1197
|
-
stopCloudPairCountdown();
|
|
1198
|
-
var ch = document.getElementById('cloud-hint');
|
|
1199
|
-
if (ch) ch.textContent = 'Bağlı. Uzak MCP oturumunda fmcp_cloud_bind çağrılmalı.';
|
|
1200
|
-
console.log('[F-MCP ATezer Bridge] Cloud handshake OK — ' + (msg.bridgeVersion || '?'));
|
|
1201
|
-
updateStatus('cloud ready', true, false);
|
|
1202
|
-
return;
|
|
1203
|
-
}
|
|
1204
|
-
var port = candidatePorts[Math.min(candidatePortIndex, candidatePorts.length - 1)];
|
|
1205
|
-
mcpConnectedPort = msg.port || port;
|
|
1206
|
-
try { localStorage.setItem(MCP_BRIDGE_PORT_KEY, String(mcpConnectedPort)); } catch (e) {}
|
|
1207
|
-
var portEl = document.getElementById('mcp-port');
|
|
1208
|
-
if (portEl && mcpConnectedPort >= MCP_PORT_MIN && mcpConnectedPort <= MCP_PORT_MAX) {
|
|
1209
|
-
portEl.value = String(mcpConnectedPort);
|
|
1210
|
-
}
|
|
1211
|
-
if (!manualPortLocked) {
|
|
1212
|
-
candidatePorts = [mcpConnectedPort];
|
|
1213
|
-
candidatePortIndex = 0;
|
|
1214
|
-
}
|
|
1215
|
-
console.log('[F-MCP ATezer Bridge] Handshake OK — bridge v' + (msg.bridgeVersion || '?') + ' on port ' + mcpConnectedPort);
|
|
1216
|
-
updateStatus('ready (:' + mcpConnectedPort + ')', true, false);
|
|
1217
|
-
return;
|
|
1218
|
-
}
|
|
1219
|
-
if (msg.type === 'ping') {
|
|
1220
|
-
if (mcpBridgeWs && mcpBridgeWs.readyState === 1) {
|
|
1221
|
-
mcpBridgeWs.send(JSON.stringify({ type: 'pong' }));
|
|
1222
|
-
}
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1225
|
-
if (!msg.id || !msg.method) return;
|
|
1226
|
-
var id = msg.id;
|
|
1227
|
-
var method = msg.method;
|
|
1228
|
-
var params = msg.params || {};
|
|
1229
|
-
var run = function() {
|
|
1230
|
-
if (method === 'getVariablesFromPluginUI') {
|
|
1231
|
-
return window.refreshVariables().then(function(r) {
|
|
1232
|
-
if (r && r.success === false) return { success: false, error: r.error || 'Variables refresh failed' };
|
|
1233
|
-
if (!window.__figmaVariablesData) return { success: false, error: 'Variables not loaded' };
|
|
1234
|
-
return { success: true, variables: window.__figmaVariablesData.variables, variableCollections: window.__figmaVariablesData.variableCollections };
|
|
1235
|
-
});
|
|
1236
|
-
}
|
|
1237
|
-
if (method === 'getComponentFromPluginUI') {
|
|
1238
|
-
return window.requestComponentData(params.nodeId).then(function(data) {
|
|
1239
|
-
return { success: true, component: data };
|
|
1240
|
-
});
|
|
1241
|
-
}
|
|
1242
|
-
if (method === 'getComponentByNodeId') {
|
|
1243
|
-
return window.requestComponentData(params.nodeId).then(function(data) {
|
|
1244
|
-
return { success: true, component: data };
|
|
1245
|
-
});
|
|
1246
|
-
}
|
|
1247
|
-
if (method === 'getVariables') {
|
|
1248
|
-
if (!window.__figmaVariablesReady || !window.__figmaVariablesData)
|
|
1249
|
-
return { success: false, error: 'Variables not loaded yet' };
|
|
1250
|
-
return { success: true, timestamp: Date.now(), fileMetadata: {}, variables: window.__figmaVariablesData.variables, variableCollections: window.__figmaVariablesData.variableCollections };
|
|
1251
|
-
}
|
|
1252
|
-
if (method === 'executeCodeViaUI') {
|
|
1253
|
-
return window.executeCode(params.code, params.timeout);
|
|
1254
|
-
}
|
|
1255
|
-
if (method === 'updateVariable') {
|
|
1256
|
-
return window.updateVariable(params.variableId, params.modeId, params.value);
|
|
1257
|
-
}
|
|
1258
|
-
if (method === 'createVariable') {
|
|
1259
|
-
return window.createVariable(params.name, params.collectionId, params.resolvedType, params.options);
|
|
1260
|
-
}
|
|
1261
|
-
if (method === 'createVariableCollection') {
|
|
1262
|
-
return window.createVariableCollection(params.name, params.options);
|
|
1263
|
-
}
|
|
1264
|
-
if (method === 'deleteVariable') {
|
|
1265
|
-
return window.deleteVariable(params.variableId);
|
|
1266
|
-
}
|
|
1267
|
-
if (method === 'deleteVariableCollection') {
|
|
1268
|
-
return window.deleteVariableCollection(params.collectionId);
|
|
1269
|
-
}
|
|
1270
|
-
if (method === 'renameVariable') {
|
|
1271
|
-
return window.renameVariable(params.variableId, params.newName);
|
|
1272
|
-
}
|
|
1273
|
-
if (method === 'addMode') {
|
|
1274
|
-
return window.addMode(params.collectionId, params.modeName);
|
|
1275
|
-
}
|
|
1276
|
-
if (method === 'renameMode') {
|
|
1277
|
-
return window.renameMode(params.collectionId, params.modeId, params.newName);
|
|
1278
|
-
}
|
|
1279
|
-
if (method === 'refreshVariables') {
|
|
1280
|
-
return window.refreshVariables();
|
|
1281
|
-
}
|
|
1282
|
-
if (method === 'getLocalComponents') {
|
|
1283
|
-
return window.getLocalComponents(params.currentPageOnly, params.limit);
|
|
1284
|
-
}
|
|
1285
|
-
if (method === 'searchLibraryAssets') {
|
|
1286
|
-
return window.searchLibraryAssets(params.query, params.assetTypes, params.limit, params.currentPageOnly);
|
|
1287
|
-
}
|
|
1288
|
-
if (method === 'getCodeConnectHints') {
|
|
1289
|
-
return window.getCodeConnectHints(params.nodeIds, params.scanCurrentPage, params.maxNodes);
|
|
1290
|
-
}
|
|
1291
|
-
if (method === 'instantiateComponent') {
|
|
1292
|
-
return window.instantiateComponent(params.componentKey, params.options);
|
|
1293
|
-
}
|
|
1294
|
-
if (method === 'setNodeDescription') {
|
|
1295
|
-
return window.setNodeDescription(params.nodeId, params.description, params.descriptionMarkdown);
|
|
1296
|
-
}
|
|
1297
|
-
if (method === 'addComponentProperty') {
|
|
1298
|
-
return window.addComponentProperty(params.nodeId, params.propertyName, params.type, params.defaultValue, params.options);
|
|
1299
|
-
}
|
|
1300
|
-
if (method === 'editComponentProperty') {
|
|
1301
|
-
return window.editComponentProperty(params.nodeId, params.propertyName, params.newValue);
|
|
1302
|
-
}
|
|
1303
|
-
if (method === 'deleteComponentProperty') {
|
|
1304
|
-
return window.deleteComponentProperty(params.nodeId, params.propertyName);
|
|
1305
|
-
}
|
|
1306
|
-
if (method === 'resizeNode') {
|
|
1307
|
-
return window.resizeNode(params.nodeId, params.width, params.height, params.withConstraints);
|
|
1308
|
-
}
|
|
1309
|
-
if (method === 'moveNode') {
|
|
1310
|
-
return window.moveNode(params.nodeId, params.x, params.y);
|
|
1311
|
-
}
|
|
1312
|
-
if (method === 'setNodeFills') {
|
|
1313
|
-
return window.setNodeFills(params.nodeId, params.fills);
|
|
1314
|
-
}
|
|
1315
|
-
if (method === 'setNodeStrokes') {
|
|
1316
|
-
return window.setNodeStrokes(params.nodeId, params.strokes, params.strokeWeight);
|
|
1317
|
-
}
|
|
1318
|
-
if (method === 'setNodeOpacity') {
|
|
1319
|
-
return window.setNodeOpacity(params.nodeId, params.opacity);
|
|
1320
|
-
}
|
|
1321
|
-
if (method === 'setNodeCornerRadius') {
|
|
1322
|
-
return window.setNodeCornerRadius(params.nodeId, params.radius);
|
|
1323
|
-
}
|
|
1324
|
-
if (method === 'cloneNode') {
|
|
1325
|
-
return window.cloneNode(params.nodeId);
|
|
1326
|
-
}
|
|
1327
|
-
if (method === 'deleteNode') {
|
|
1328
|
-
return window.deleteNode(params.nodeId);
|
|
1329
|
-
}
|
|
1330
|
-
if (method === 'renameNode') {
|
|
1331
|
-
return window.renameNode(params.nodeId, params.newName);
|
|
1332
|
-
}
|
|
1333
|
-
if (method === 'setTextContent') {
|
|
1334
|
-
return window.setTextContent(params.nodeId, params.text, params.options);
|
|
1335
|
-
}
|
|
1336
|
-
if (method === 'createChildNode') {
|
|
1337
|
-
return window.createChildNode(params.parentId, params.nodeType, params.properties);
|
|
1338
|
-
}
|
|
1339
|
-
if (method === 'captureScreenshot') {
|
|
1340
|
-
return window.captureScreenshot(params.nodeId, params.options);
|
|
1341
|
-
}
|
|
1342
|
-
if (method === 'setInstanceProperties') {
|
|
1343
|
-
return window.setInstanceProperties(params.nodeId, params.properties);
|
|
1344
|
-
}
|
|
1345
|
-
if (method === 'getDocumentStructure') {
|
|
1346
|
-
return window.getDocumentStructure(params.depth, params.verbosity, params.includeLayout, params.includeVisual, params.includeTypography, params.includeCodeReady, params.outputHint);
|
|
1347
|
-
}
|
|
1348
|
-
if (method === 'getNodeContext') {
|
|
1349
|
-
return window.getNodeContext(params.nodeId, params.depth, params.verbosity, params.includeLayout, params.includeVisual, params.includeTypography, params.includeCodeReady, params.outputHint);
|
|
1350
|
-
}
|
|
1351
|
-
if (method === 'getLocalStyles') {
|
|
1352
|
-
return window.getLocalStyles(params.verbosity);
|
|
1353
|
-
}
|
|
1354
|
-
if (method === 'getConsoleLogs') {
|
|
1355
|
-
return window.getConsoleLogs(params.limit);
|
|
1356
|
-
}
|
|
1357
|
-
if (method === 'clearConsole') {
|
|
1358
|
-
return window.clearConsole();
|
|
1359
|
-
}
|
|
1360
|
-
if (method === 'batchCreateVariables') {
|
|
1361
|
-
return window.batchCreateVariables(params.items);
|
|
1362
|
-
}
|
|
1363
|
-
if (method === 'batchUpdateVariables') {
|
|
1364
|
-
return window.batchUpdateVariables(params.items);
|
|
1365
|
-
}
|
|
1366
|
-
if (method === 'setupDesignTokens') {
|
|
1367
|
-
return window.setupDesignTokens(params);
|
|
1368
|
-
}
|
|
1369
|
-
if (method === 'arrangeComponentSet') {
|
|
1370
|
-
return window.arrangeComponentSet(params.nodeIds);
|
|
1371
|
-
}
|
|
1372
|
-
return { success: false, error: 'Unknown method: ' + method };
|
|
1373
|
-
};
|
|
1374
|
-
var wsRef = mcpBridgeWs;
|
|
1375
|
-
Promise.resolve(run()).then(function(result) {
|
|
1376
|
-
if (wsRef && wsRef.readyState === 1) {
|
|
1377
|
-
try { wsRef.send(JSON.stringify({ id: id, result: result })); } catch (_) {}
|
|
1378
|
-
}
|
|
1379
|
-
}).catch(function(err) {
|
|
1380
|
-
if (wsRef && wsRef.readyState === 1) {
|
|
1381
|
-
try { wsRef.send(JSON.stringify({ id: id, error: err.message || String(err) })); } catch (_) {}
|
|
1382
|
-
}
|
|
1383
|
-
});
|
|
1384
|
-
} catch (e) {
|
|
1385
|
-
if (mcpBridgeWs && mcpBridgeWs.readyState === 1 && msg && msg.id) {
|
|
1386
|
-
try { mcpBridgeWs.send(JSON.stringify({ id: msg.id, error: e.message || String(e) })); } catch (_) {}
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
function connectCloudMcpBridge() {
|
|
1392
|
-
if (mcpConnecting) return;
|
|
1393
|
-
if (mcpBridgeWs && (mcpBridgeWs.readyState === 0 || mcpBridgeWs.readyState === 1)) return;
|
|
1394
|
-
var url = buildCloudWsUrl();
|
|
1395
|
-
if (!url) {
|
|
1396
|
-
updateStatus('cloud: eksik alan', false, true);
|
|
1397
|
-
return;
|
|
1398
|
-
}
|
|
1399
|
-
startCloudPairCountdown();
|
|
1400
|
-
mcpConnecting = true;
|
|
1401
|
-
mcpHandshakeDone = false;
|
|
1402
|
-
autoPortProbeActive = false;
|
|
1403
|
-
try {
|
|
1404
|
-
mcpBridgeWs = new WebSocket(url);
|
|
1405
|
-
var currentWs = mcpBridgeWs;
|
|
1406
|
-
mcpBridgeWs.onopen = function() {
|
|
1407
|
-
mcpConnecting = false;
|
|
1408
|
-
mcpReconnectDelay = MCP_RECONNECT_MIN;
|
|
1409
|
-
console.log('[F-MCP ATezer Bridge] Cloud WebSocket: ' + url);
|
|
1410
|
-
updateStatus('cloud…', false, false);
|
|
1411
|
-
currentWs.send(JSON.stringify({
|
|
1412
|
-
type: 'ready',
|
|
1413
|
-
fileKey: window.__figmaFileKey || null,
|
|
1414
|
-
fileName: window.__figmaFileName || null
|
|
1415
|
-
}));
|
|
1416
|
-
startKeepAlive(currentWs);
|
|
1417
|
-
if (mcpHandshakeTimer) clearTimeout(mcpHandshakeTimer);
|
|
1418
|
-
mcpHandshakeTimer = setTimeout(function() {
|
|
1419
|
-
if (!mcpHandshakeDone && mcpBridgeWs === currentWs) {
|
|
1420
|
-
updateStatus('cloud handshake?', false, true);
|
|
1421
|
-
}
|
|
1422
|
-
}, 8000);
|
|
1423
|
-
};
|
|
1424
|
-
mcpBridgeWs.onmessage = handleMcpBridgeMessage;
|
|
1425
|
-
mcpBridgeWs.onclose = function() {
|
|
1426
|
-
stopKeepAlive();
|
|
1427
|
-
stopCloudPairCountdown();
|
|
1428
|
-
resetCloudHintDefault();
|
|
1429
|
-
if (mcpBridgeWs !== currentWs) return;
|
|
1430
|
-
mcpBridgeWs = null;
|
|
1431
|
-
mcpConnecting = false;
|
|
1432
|
-
if (mcpHandshakeTimer) { clearTimeout(mcpHandshakeTimer); mcpHandshakeTimer = null; }
|
|
1433
|
-
updateStatus('cloud kapalı', false, true);
|
|
1434
|
-
scheduleReconnect();
|
|
1435
|
-
};
|
|
1436
|
-
mcpBridgeWs.onerror = function() {
|
|
1437
|
-
mcpConnecting = false;
|
|
1438
|
-
};
|
|
1439
|
-
} catch (e) {
|
|
1440
|
-
stopCloudPairCountdown();
|
|
1441
|
-
resetCloudHintDefault();
|
|
1442
|
-
mcpConnecting = false;
|
|
1443
|
-
updateStatus('cloud hata', false, true);
|
|
1444
|
-
scheduleReconnect();
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
1007
|
function connectMcpBridge() {
|
|
1449
|
-
if (isCloudMode()) {
|
|
1450
|
-
connectCloudMcpBridge();
|
|
1451
|
-
return;
|
|
1452
|
-
}
|
|
1453
1008
|
if (mcpConnecting) return;
|
|
1454
1009
|
if (mcpBridgeWs && (mcpBridgeWs.readyState === 0 || mcpBridgeWs.readyState === 1)) return;
|
|
1455
1010
|
mcpConnecting = true;
|
|
@@ -1484,7 +1039,198 @@
|
|
|
1484
1039
|
}
|
|
1485
1040
|
}, 5000);
|
|
1486
1041
|
};
|
|
1487
|
-
mcpBridgeWs.onmessage =
|
|
1042
|
+
mcpBridgeWs.onmessage = function(event) {
|
|
1043
|
+
try {
|
|
1044
|
+
var msg = JSON.parse(event.data);
|
|
1045
|
+
if (msg.type === 'welcome') {
|
|
1046
|
+
mcpHandshakeDone = true;
|
|
1047
|
+
if (mcpHandshakeTimer) { clearTimeout(mcpHandshakeTimer); mcpHandshakeTimer = null; }
|
|
1048
|
+
mcpConnectedPort = msg.port || port;
|
|
1049
|
+
try { localStorage.setItem(MCP_BRIDGE_PORT_KEY, String(mcpConnectedPort)); } catch (e) {}
|
|
1050
|
+
var portEl = document.getElementById('mcp-port');
|
|
1051
|
+
if (portEl && mcpConnectedPort >= MCP_PORT_MIN && mcpConnectedPort <= MCP_PORT_MAX) {
|
|
1052
|
+
portEl.value = String(mcpConnectedPort);
|
|
1053
|
+
}
|
|
1054
|
+
if (!manualPortLocked) {
|
|
1055
|
+
candidatePorts = [mcpConnectedPort];
|
|
1056
|
+
candidatePortIndex = 0;
|
|
1057
|
+
}
|
|
1058
|
+
console.log('[F-MCP ATezer Bridge] Handshake OK — bridge v' + (msg.bridgeVersion || '?') + ' on port ' + mcpConnectedPort);
|
|
1059
|
+
updateStatus('ready (:' + mcpConnectedPort + ')', true, false);
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
if (msg.type === 'ping') {
|
|
1063
|
+
if (mcpBridgeWs && mcpBridgeWs.readyState === 1) {
|
|
1064
|
+
mcpBridgeWs.send(JSON.stringify({ type: 'pong' }));
|
|
1065
|
+
}
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
if (!msg.id || !msg.method) return;
|
|
1069
|
+
var id = msg.id;
|
|
1070
|
+
var method = msg.method;
|
|
1071
|
+
var params = msg.params || {};
|
|
1072
|
+
var run = function() {
|
|
1073
|
+
if (method === 'getVariablesFromPluginUI') {
|
|
1074
|
+
// Always refresh first (async API) so variables work in dynamic-page context
|
|
1075
|
+
return window.refreshVariables().then(function(r) {
|
|
1076
|
+
if (r && r.success === false) return { success: false, error: r.error || 'Variables refresh failed' };
|
|
1077
|
+
if (!window.__figmaVariablesData) return { success: false, error: 'Variables not loaded' };
|
|
1078
|
+
return { success: true, variables: window.__figmaVariablesData.variables, variableCollections: window.__figmaVariablesData.variableCollections };
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
if (method === 'getComponentFromPluginUI') {
|
|
1082
|
+
return window.requestComponentData(params.nodeId).then(function(data) {
|
|
1083
|
+
return { success: true, component: data };
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
if (method === 'getComponentByNodeId') {
|
|
1087
|
+
return window.requestComponentData(params.nodeId).then(function(data) {
|
|
1088
|
+
return { success: true, component: data };
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
if (method === 'getVariables') {
|
|
1092
|
+
if (!window.__figmaVariablesReady || !window.__figmaVariablesData)
|
|
1093
|
+
return { success: false, error: 'Variables not loaded yet' };
|
|
1094
|
+
return { success: true, timestamp: Date.now(), fileMetadata: {}, variables: window.__figmaVariablesData.variables, variableCollections: window.__figmaVariablesData.variableCollections };
|
|
1095
|
+
}
|
|
1096
|
+
if (method === 'executeCodeViaUI') {
|
|
1097
|
+
return window.executeCode(params.code, params.timeout);
|
|
1098
|
+
}
|
|
1099
|
+
if (method === 'updateVariable') {
|
|
1100
|
+
return window.updateVariable(params.variableId, params.modeId, params.value);
|
|
1101
|
+
}
|
|
1102
|
+
if (method === 'createVariable') {
|
|
1103
|
+
return window.createVariable(params.name, params.collectionId, params.resolvedType, params.options);
|
|
1104
|
+
}
|
|
1105
|
+
if (method === 'createVariableCollection') {
|
|
1106
|
+
return window.createVariableCollection(params.name, params.options);
|
|
1107
|
+
}
|
|
1108
|
+
if (method === 'deleteVariable') {
|
|
1109
|
+
return window.deleteVariable(params.variableId);
|
|
1110
|
+
}
|
|
1111
|
+
if (method === 'deleteVariableCollection') {
|
|
1112
|
+
return window.deleteVariableCollection(params.collectionId);
|
|
1113
|
+
}
|
|
1114
|
+
if (method === 'renameVariable') {
|
|
1115
|
+
return window.renameVariable(params.variableId, params.newName);
|
|
1116
|
+
}
|
|
1117
|
+
if (method === 'addMode') {
|
|
1118
|
+
return window.addMode(params.collectionId, params.modeName);
|
|
1119
|
+
}
|
|
1120
|
+
if (method === 'renameMode') {
|
|
1121
|
+
return window.renameMode(params.collectionId, params.modeId, params.newName);
|
|
1122
|
+
}
|
|
1123
|
+
if (method === 'refreshVariables') {
|
|
1124
|
+
return window.refreshVariables();
|
|
1125
|
+
}
|
|
1126
|
+
if (method === 'getLocalComponents') {
|
|
1127
|
+
return window.getLocalComponents(params.currentPageOnly, params.limit);
|
|
1128
|
+
}
|
|
1129
|
+
if (method === 'searchLibraryAssets') {
|
|
1130
|
+
return window.searchLibraryAssets(params.query, params.assetTypes, params.limit, params.currentPageOnly);
|
|
1131
|
+
}
|
|
1132
|
+
if (method === 'getCodeConnectHints') {
|
|
1133
|
+
return window.getCodeConnectHints(params.nodeIds, params.scanCurrentPage, params.maxNodes);
|
|
1134
|
+
}
|
|
1135
|
+
if (method === 'instantiateComponent') {
|
|
1136
|
+
return window.instantiateComponent(params.componentKey, params.options);
|
|
1137
|
+
}
|
|
1138
|
+
if (method === 'setNodeDescription') {
|
|
1139
|
+
return window.setNodeDescription(params.nodeId, params.description, params.descriptionMarkdown);
|
|
1140
|
+
}
|
|
1141
|
+
if (method === 'addComponentProperty') {
|
|
1142
|
+
return window.addComponentProperty(params.nodeId, params.propertyName, params.type, params.defaultValue, params.options);
|
|
1143
|
+
}
|
|
1144
|
+
if (method === 'editComponentProperty') {
|
|
1145
|
+
return window.editComponentProperty(params.nodeId, params.propertyName, params.newValue);
|
|
1146
|
+
}
|
|
1147
|
+
if (method === 'deleteComponentProperty') {
|
|
1148
|
+
return window.deleteComponentProperty(params.nodeId, params.propertyName);
|
|
1149
|
+
}
|
|
1150
|
+
if (method === 'resizeNode') {
|
|
1151
|
+
return window.resizeNode(params.nodeId, params.width, params.height, params.withConstraints);
|
|
1152
|
+
}
|
|
1153
|
+
if (method === 'moveNode') {
|
|
1154
|
+
return window.moveNode(params.nodeId, params.x, params.y);
|
|
1155
|
+
}
|
|
1156
|
+
if (method === 'setNodeFills') {
|
|
1157
|
+
return window.setNodeFills(params.nodeId, params.fills);
|
|
1158
|
+
}
|
|
1159
|
+
if (method === 'setNodeStrokes') {
|
|
1160
|
+
return window.setNodeStrokes(params.nodeId, params.strokes, params.strokeWeight);
|
|
1161
|
+
}
|
|
1162
|
+
if (method === 'setNodeOpacity') {
|
|
1163
|
+
return window.setNodeOpacity(params.nodeId, params.opacity);
|
|
1164
|
+
}
|
|
1165
|
+
if (method === 'setNodeCornerRadius') {
|
|
1166
|
+
return window.setNodeCornerRadius(params.nodeId, params.radius);
|
|
1167
|
+
}
|
|
1168
|
+
if (method === 'cloneNode') {
|
|
1169
|
+
return window.cloneNode(params.nodeId);
|
|
1170
|
+
}
|
|
1171
|
+
if (method === 'deleteNode') {
|
|
1172
|
+
return window.deleteNode(params.nodeId);
|
|
1173
|
+
}
|
|
1174
|
+
if (method === 'renameNode') {
|
|
1175
|
+
return window.renameNode(params.nodeId, params.newName);
|
|
1176
|
+
}
|
|
1177
|
+
if (method === 'setTextContent') {
|
|
1178
|
+
return window.setTextContent(params.nodeId, params.text, params.options);
|
|
1179
|
+
}
|
|
1180
|
+
if (method === 'createChildNode') {
|
|
1181
|
+
return window.createChildNode(params.parentId, params.nodeType, params.properties);
|
|
1182
|
+
}
|
|
1183
|
+
if (method === 'captureScreenshot') {
|
|
1184
|
+
return window.captureScreenshot(params.nodeId, params.options);
|
|
1185
|
+
}
|
|
1186
|
+
if (method === 'setInstanceProperties') {
|
|
1187
|
+
return window.setInstanceProperties(params.nodeId, params.properties);
|
|
1188
|
+
}
|
|
1189
|
+
if (method === 'getDocumentStructure') {
|
|
1190
|
+
return window.getDocumentStructure(params.depth, params.verbosity, params.includeLayout, params.includeVisual, params.includeTypography, params.includeCodeReady, params.outputHint);
|
|
1191
|
+
}
|
|
1192
|
+
if (method === 'getNodeContext') {
|
|
1193
|
+
return window.getNodeContext(params.nodeId, params.depth, params.verbosity, params.includeLayout, params.includeVisual, params.includeTypography, params.includeCodeReady, params.outputHint);
|
|
1194
|
+
}
|
|
1195
|
+
if (method === 'getLocalStyles') {
|
|
1196
|
+
return window.getLocalStyles(params.verbosity);
|
|
1197
|
+
}
|
|
1198
|
+
if (method === 'getConsoleLogs') {
|
|
1199
|
+
return window.getConsoleLogs(params.limit);
|
|
1200
|
+
}
|
|
1201
|
+
if (method === 'clearConsole') {
|
|
1202
|
+
return window.clearConsole();
|
|
1203
|
+
}
|
|
1204
|
+
if (method === 'batchCreateVariables') {
|
|
1205
|
+
return window.batchCreateVariables(params.items);
|
|
1206
|
+
}
|
|
1207
|
+
if (method === 'batchUpdateVariables') {
|
|
1208
|
+
return window.batchUpdateVariables(params.items);
|
|
1209
|
+
}
|
|
1210
|
+
if (method === 'setupDesignTokens') {
|
|
1211
|
+
return window.setupDesignTokens(params);
|
|
1212
|
+
}
|
|
1213
|
+
if (method === 'arrangeComponentSet') {
|
|
1214
|
+
return window.arrangeComponentSet(params.nodeIds);
|
|
1215
|
+
}
|
|
1216
|
+
return { success: false, error: 'Unknown method: ' + method };
|
|
1217
|
+
};
|
|
1218
|
+
var wsRef = mcpBridgeWs;
|
|
1219
|
+
Promise.resolve(run()).then(function(result) {
|
|
1220
|
+
if (wsRef && wsRef.readyState === 1) {
|
|
1221
|
+
try { wsRef.send(JSON.stringify({ id: id, result: result })); } catch (_) {}
|
|
1222
|
+
}
|
|
1223
|
+
}).catch(function(err) {
|
|
1224
|
+
if (wsRef && wsRef.readyState === 1) {
|
|
1225
|
+
try { wsRef.send(JSON.stringify({ id: id, error: err.message || String(err) })); } catch (_) {}
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
} catch (e) {
|
|
1229
|
+
if (mcpBridgeWs && mcpBridgeWs.readyState === 1 && msg && msg.id) {
|
|
1230
|
+
try { mcpBridgeWs.send(JSON.stringify({ id: msg.id, error: e.message || String(e) })); } catch (_) {}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1488
1234
|
mcpBridgeWs.onclose = function() {
|
|
1489
1235
|
stopKeepAlive();
|
|
1490
1236
|
if (mcpBridgeWs !== currentWs) return;
|
|
@@ -1520,7 +1266,6 @@
|
|
|
1520
1266
|
}
|
|
1521
1267
|
|
|
1522
1268
|
if (typeof WebSocket !== 'undefined') {
|
|
1523
|
-
initCloudModeUi();
|
|
1524
1269
|
initMcpHostInput();
|
|
1525
1270
|
initMcpPortInput();
|
|
1526
1271
|
initAdvancedPanel();
|
|
@@ -1530,9 +1275,7 @@
|
|
|
1530
1275
|
DEFAULT_MCP_PORT = rememberedPort;
|
|
1531
1276
|
}
|
|
1532
1277
|
} catch (e) {}
|
|
1533
|
-
|
|
1534
|
-
candidatePorts = listCandidatePorts();
|
|
1535
|
-
}
|
|
1278
|
+
candidatePorts = listCandidatePorts();
|
|
1536
1279
|
connectMcpBridge();
|
|
1537
1280
|
requestUiResize();
|
|
1538
1281
|
}
|