figma-rust-mcp 2.8.3 → 2.8.4
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/bin/figma-mcp.js +1 -1
- package/package.json +1 -1
- package/plugin/code.js +86 -12
- package/plugin/ui.html +120 -27
package/bin/figma-mcp.js
CHANGED
|
@@ -19,7 +19,7 @@ const __dirname = path.dirname(__filename);
|
|
|
19
19
|
|
|
20
20
|
// Read package.json version
|
|
21
21
|
const pkgPath = path.resolve(__dirname, '../package.json');
|
|
22
|
-
let pkgVersion = '2.8.
|
|
22
|
+
let pkgVersion = '2.8.4';
|
|
23
23
|
let repoOwner = 'BuiHung1612';
|
|
24
24
|
let repoName = 'figma-mcp';
|
|
25
25
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "figma-rust-mcp",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.4",
|
|
4
4
|
"description": "Claude Code to Figma — Bidirectional MCP bridge. AI draws UI on Figma canvas, reads designs back. Works with Claude, Cursor, VS Code, Windsurf, Antigravity. No API key needed.",
|
|
5
5
|
"mcpName": "io.github.BuiHung1612/figma-mcp",
|
|
6
6
|
"type": "module",
|
package/plugin/code.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// figma-mcp — Figma Plugin main thread v1.2.1
|
|
2
2
|
// Handles both WRITE (draw UI) and READ (extract design) operations.
|
|
3
3
|
// AUTO-GENERATED by scripts/build-plugin.js — edit plugin-src/*.js instead
|
|
4
|
-
// Generated: 2026-08-
|
|
4
|
+
// Generated: 2026-08-26T04:26:26.667Z
|
|
5
5
|
|
|
6
6
|
// ── plugin-src/utils.js ──
|
|
7
7
|
// ─── UTILS ────────────────────────────────────────────────────────────────────
|
|
@@ -1238,7 +1238,7 @@ const handlers = {};
|
|
|
1238
1238
|
|
|
1239
1239
|
handlers.status = async () => ({
|
|
1240
1240
|
connected: true,
|
|
1241
|
-
version: "2.8.
|
|
1241
|
+
version: "2.8.3",
|
|
1242
1242
|
fileName: figma.root.name,
|
|
1243
1243
|
currentPage: figma.currentPage.name,
|
|
1244
1244
|
pageCount: figma.root.children.length,
|
|
@@ -3010,10 +3010,26 @@ handlers.export_image = async function(params) {
|
|
|
3010
3010
|
handlers.index_scan = async function() {
|
|
3011
3011
|
var pageNodesPromise = (async function() {
|
|
3012
3012
|
try {
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3013
|
+
var page = figma.currentPage;
|
|
3014
|
+
var topFrames = page.children || [];
|
|
3015
|
+
var collected = [];
|
|
3016
|
+
var CHUNK_SIZE = 10;
|
|
3017
|
+
|
|
3018
|
+
for (var i = 0; i < topFrames.length; i += CHUNK_SIZE) {
|
|
3019
|
+
var chunk = topFrames.slice(i, i + CHUNK_SIZE);
|
|
3020
|
+
var chunkData = chunk.map(function(n) {
|
|
3021
|
+
return Object.assign(nodeToInfo(n), { childCount: "children" in n ? n.children.length : 0 });
|
|
3022
|
+
});
|
|
3023
|
+
collected = collected.concat(chunkData);
|
|
3024
|
+
|
|
3025
|
+
// Stream chunk progressively if there are multiple chunks
|
|
3026
|
+
if (topFrames.length > CHUNK_SIZE && figma.ui) {
|
|
3027
|
+
try {
|
|
3028
|
+
figma.ui.postMessage({ type: "index-chunk", nodes: chunkData });
|
|
3029
|
+
} catch(e3) {}
|
|
3030
|
+
}
|
|
3016
3031
|
}
|
|
3032
|
+
return collected;
|
|
3017
3033
|
} catch (e) {}
|
|
3018
3034
|
return [];
|
|
3019
3035
|
})();
|
|
@@ -5157,14 +5173,43 @@ figma.on("selectionchange", function() {
|
|
|
5157
5173
|
} catch (e) {}
|
|
5158
5174
|
});
|
|
5159
5175
|
|
|
5160
|
-
// Broadcast document changes to invalidate index cache
|
|
5176
|
+
// Broadcast granular document changes to invalidate or incrementally update index cache
|
|
5161
5177
|
var docChangeTimer = null;
|
|
5162
|
-
|
|
5178
|
+
var pendingChangedNodeIds = new Set();
|
|
5179
|
+
|
|
5180
|
+
function onDocChange(event) {
|
|
5163
5181
|
try {
|
|
5182
|
+
if (event && event.documentChanges) {
|
|
5183
|
+
for (var i = 0; i < event.documentChanges.length; i++) {
|
|
5184
|
+
var ch = event.documentChanges[i];
|
|
5185
|
+
if (ch && ch.id) pendingChangedNodeIds.add(ch.id);
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5164
5188
|
if (docChangeTimer) clearTimeout(docChangeTimer);
|
|
5165
|
-
docChangeTimer = setTimeout(function() {
|
|
5189
|
+
docChangeTimer = setTimeout(async function() {
|
|
5190
|
+
var ids = Array.from(pendingChangedNodeIds);
|
|
5191
|
+
pendingChangedNodeIds.clear();
|
|
5192
|
+
|
|
5193
|
+
if (ids.length > 0 && ids.length <= 15) {
|
|
5194
|
+
// Incremental micro-diff update for fast real-time editing
|
|
5195
|
+
var diffNodes = [];
|
|
5196
|
+
for (var j = 0; j < ids.length; j++) {
|
|
5197
|
+
try {
|
|
5198
|
+
var n = await findNodeByIdAsync(ids[j]);
|
|
5199
|
+
if (n && typeof nodeToInfo === "function") {
|
|
5200
|
+
diffNodes.push(nodeToInfo(n));
|
|
5201
|
+
}
|
|
5202
|
+
} catch(e2) {}
|
|
5203
|
+
}
|
|
5204
|
+
if (diffNodes.length > 0) {
|
|
5205
|
+
figma.ui.postMessage({ type: "node-diff", nodes: diffNodes });
|
|
5206
|
+
return;
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
|
|
5210
|
+
// Fallback for large batch changes: mark dirty or trigger background sync
|
|
5166
5211
|
figma.ui.postMessage({ type: "document-change" });
|
|
5167
|
-
},
|
|
5212
|
+
}, 800);
|
|
5168
5213
|
} catch (e) {}
|
|
5169
5214
|
}
|
|
5170
5215
|
|
|
@@ -5230,7 +5275,33 @@ figma.ui.onmessage = async (request) => {
|
|
|
5230
5275
|
var newH = Math.max(200, Math.min(1200, Math.round(request.height)));
|
|
5231
5276
|
try {
|
|
5232
5277
|
figma.ui.resize(newW, newH);
|
|
5233
|
-
|
|
5278
|
+
} catch(e) {}
|
|
5279
|
+
return;
|
|
5280
|
+
}
|
|
5281
|
+
|
|
5282
|
+
if (request.type === "save-window-size") {
|
|
5283
|
+
var sw = Math.max(260, Math.min(1000, Math.round(request.width)));
|
|
5284
|
+
var sh = Math.max(200, Math.min(1200, Math.round(request.height)));
|
|
5285
|
+
try {
|
|
5286
|
+
figma.clientStorage.setAsync("mcp_window_size", { width: sw, height: sh }).catch(function() {});
|
|
5287
|
+
} catch(e) {}
|
|
5288
|
+
return;
|
|
5289
|
+
}
|
|
5290
|
+
|
|
5291
|
+
// Handle manual reindex request from UI
|
|
5292
|
+
if (request.type === "manual-reindex") {
|
|
5293
|
+
try {
|
|
5294
|
+
if (handlers.index_scan) {
|
|
5295
|
+
var startMs = Date.now();
|
|
5296
|
+
var scanResult = await handlers.index_scan();
|
|
5297
|
+
figma.ui.postMessage({
|
|
5298
|
+
type: "index-update",
|
|
5299
|
+
data: scanResult,
|
|
5300
|
+
fileName: figma.root ? figma.root.name : "unknown",
|
|
5301
|
+
sessionId: figma.root ? figma.root.id : "_default",
|
|
5302
|
+
startMs: startMs
|
|
5303
|
+
});
|
|
5304
|
+
}
|
|
5234
5305
|
} catch(e) {}
|
|
5235
5306
|
return;
|
|
5236
5307
|
}
|
|
@@ -5246,12 +5317,15 @@ figma.ui.onmessage = async (request) => {
|
|
|
5246
5317
|
return;
|
|
5247
5318
|
}
|
|
5248
5319
|
|
|
5320
|
+
const startTime = Date.now();
|
|
5249
5321
|
try {
|
|
5250
5322
|
var data = await handler(params || {});
|
|
5251
|
-
|
|
5323
|
+
var durationMs = Date.now() - startTime;
|
|
5324
|
+
figma.ui.postMessage({ id: id, operation: operation, success: true, dataJson: stringifyForBridge(data), durationMs: durationMs });
|
|
5252
5325
|
} catch (err) {
|
|
5326
|
+
var durationMs = Date.now() - startTime;
|
|
5253
5327
|
var errMsg = "[dispatch:" + operation + "] " + (err && err.message ? err.message : String(err));
|
|
5254
|
-
figma.ui.postMessage({ id: id, operation: operation, success: false, error: errMsg });
|
|
5328
|
+
figma.ui.postMessage({ id: id, operation: operation, success: false, error: errMsg, durationMs: durationMs });
|
|
5255
5329
|
}
|
|
5256
5330
|
};
|
|
5257
5331
|
|
package/plugin/ui.html
CHANGED
|
@@ -140,8 +140,14 @@
|
|
|
140
140
|
/* STATS ROW */
|
|
141
141
|
.stats-row {
|
|
142
142
|
display: grid;
|
|
143
|
-
grid-template-columns:
|
|
144
|
-
gap:
|
|
143
|
+
grid-template-columns: repeat(4, 1fr);
|
|
144
|
+
gap: 5px;
|
|
145
|
+
flex-shrink: 0;
|
|
146
|
+
}
|
|
147
|
+
.stats-subrow {
|
|
148
|
+
display: grid;
|
|
149
|
+
grid-template-columns: 1fr 1fr auto;
|
|
150
|
+
gap: 5px;
|
|
145
151
|
flex-shrink: 0;
|
|
146
152
|
}
|
|
147
153
|
|
|
@@ -149,38 +155,45 @@
|
|
|
149
155
|
background: var(--surface-1);
|
|
150
156
|
border: 1px solid var(--border-1);
|
|
151
157
|
border-radius: var(--radius-sm);
|
|
152
|
-
padding: 7px
|
|
153
|
-
display: flex; flex-direction: column; gap:
|
|
158
|
+
padding: 6px 7px;
|
|
159
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
154
160
|
transition: border-color var(--transition);
|
|
155
161
|
}
|
|
156
162
|
.stat-card:hover { border-color: var(--border-2); }
|
|
157
163
|
|
|
158
164
|
.stat-label {
|
|
159
|
-
font-size:
|
|
160
|
-
letter-spacing: 0.
|
|
165
|
+
font-size: 8px; font-weight: 700;
|
|
166
|
+
letter-spacing: 0.7px; text-transform: uppercase;
|
|
161
167
|
color: var(--text-3);
|
|
162
168
|
}
|
|
163
169
|
.stat-value {
|
|
164
|
-
font-size:
|
|
170
|
+
font-size: 15px; font-weight: 800;
|
|
165
171
|
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
166
|
-
line-height: 1;
|
|
172
|
+
line-height: 1.1;
|
|
173
|
+
display: flex; align-items: baseline; gap: 2px;
|
|
174
|
+
}
|
|
175
|
+
.stat-unit {
|
|
176
|
+
font-size: 8.5px; font-weight: 600; color: var(--text-3);
|
|
167
177
|
}
|
|
168
178
|
.stat-card.writes .stat-value { color: var(--violet); }
|
|
169
179
|
.stat-card.reads .stat-value { color: var(--accent-2); }
|
|
170
180
|
.stat-card.errors .stat-value { color: var(--rose); }
|
|
181
|
+
.stat-card.speed .stat-value { color: var(--green); }
|
|
182
|
+
.stat-card.memory .stat-value { color: var(--amber); }
|
|
171
183
|
|
|
172
184
|
.port-card {
|
|
173
185
|
background: var(--surface-1);
|
|
174
186
|
border: 1px solid var(--border-1);
|
|
175
187
|
border-radius: var(--radius-sm);
|
|
176
|
-
padding:
|
|
177
|
-
display: flex; flex-direction: column; gap:
|
|
188
|
+
padding: 6px 9px;
|
|
189
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
178
190
|
cursor: pointer;
|
|
191
|
+
justify-content: center;
|
|
179
192
|
transition: border-color var(--transition), background var(--transition);
|
|
180
193
|
}
|
|
181
194
|
.port-card:hover { border-color: rgba(6,182,212,0.35); background: rgba(6,182,212,0.04); }
|
|
182
|
-
.port-label { font-size:
|
|
183
|
-
.port-value { font-size: 12px; font-weight: 700; font-family: 'SF Mono', 'Fira Code', monospace; color: var(--accent-2); line-height: 1; }
|
|
195
|
+
.port-label { font-size: 8px; font-weight: 700; letter-spacing: 0.7px; text-transform: uppercase; color: var(--text-3); }
|
|
196
|
+
.port-value { font-size: 12px; font-weight: 700; font-family: 'SF Mono', 'Fira Code', monospace; color: var(--accent-2); line-height: 1.1; }
|
|
184
197
|
|
|
185
198
|
/* TERMINAL */
|
|
186
199
|
.terminal-wrap {
|
|
@@ -290,15 +303,17 @@
|
|
|
290
303
|
|
|
291
304
|
/* RESIZE GRIP */
|
|
292
305
|
.resize-grip {
|
|
293
|
-
position: fixed; right:
|
|
294
|
-
width:
|
|
295
|
-
cursor: nwse-resize; opacity: 0.
|
|
306
|
+
position: fixed; right: 0; bottom: 0;
|
|
307
|
+
width: 24px; height: 24px;
|
|
308
|
+
cursor: nwse-resize; opacity: 0.35;
|
|
296
309
|
color: var(--text-2);
|
|
297
310
|
transition: opacity var(--transition), color var(--transition);
|
|
298
311
|
z-index: 1000;
|
|
299
312
|
display: flex; align-items: flex-end; justify-content: flex-end;
|
|
313
|
+
padding: 4px;
|
|
314
|
+
touch-action: none;
|
|
300
315
|
}
|
|
301
|
-
.resize-grip:hover { opacity:
|
|
316
|
+
.resize-grip:hover { opacity: 1; color: var(--accent-2); }
|
|
302
317
|
</style>
|
|
303
318
|
</head>
|
|
304
319
|
<body>
|
|
@@ -334,13 +349,13 @@
|
|
|
334
349
|
<span class="stat-label">Reads</span>
|
|
335
350
|
<span class="stat-value" id="read-count">0</span>
|
|
336
351
|
</div>
|
|
337
|
-
<div class="stat-card
|
|
338
|
-
<span class="stat-label">
|
|
339
|
-
<span class="stat-value" id="
|
|
352
|
+
<div class="stat-card speed" title="Last Request Execution Time / Speed">
|
|
353
|
+
<span class="stat-label">Speed</span>
|
|
354
|
+
<span class="stat-value"><span id="speed-display">0</span><span class="stat-unit">ms</span></span>
|
|
340
355
|
</div>
|
|
341
|
-
<div class="
|
|
342
|
-
<span class="
|
|
343
|
-
<span class="
|
|
356
|
+
<div class="stat-card memory" title="Server Memory Usage (RAM RSS)">
|
|
357
|
+
<span class="stat-label">RAM</span>
|
|
358
|
+
<span class="stat-value"><span id="ram-display">3.2</span><span class="stat-unit">MB</span></span>
|
|
344
359
|
</div>
|
|
345
360
|
</div>
|
|
346
361
|
|
|
@@ -367,6 +382,13 @@
|
|
|
367
382
|
</svg>
|
|
368
383
|
</button>
|
|
369
384
|
|
|
385
|
+
<button class="btn" id="reindex-btn" onclick="triggerManualReindex()" title="Re-index Figma document to Rust memory" style="background:var(--surface-2);border:1px solid var(--border-2);color:var(--text-2);font-size:10px;padding:0 10px;gap:5px;">
|
|
386
|
+
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
|
387
|
+
<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"/>
|
|
388
|
+
</svg>
|
|
389
|
+
<span id="reindex-btn-label">⚡ Index</span>
|
|
390
|
+
</button>
|
|
391
|
+
|
|
370
392
|
<button class="btn btn-primary" id="launch-btn" onclick="launchServer()" title="Start Figma MCP server">
|
|
371
393
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
372
394
|
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
|
|
@@ -416,6 +438,34 @@
|
|
|
416
438
|
var launchBtn = document.getElementById("launch-btn");
|
|
417
439
|
var launchBtnLabel = document.getElementById("launch-btn-label");
|
|
418
440
|
var statusPill = document.getElementById("status-pill");
|
|
441
|
+
var speedEl = document.getElementById("speed-display");
|
|
442
|
+
var ramEl = document.getElementById("ram-display");
|
|
443
|
+
var reindexBtn = document.getElementById("reindex-btn");
|
|
444
|
+
var reindexBtnLabel = document.getElementById("reindex-btn-label");
|
|
445
|
+
|
|
446
|
+
async function fetchServerStats() {
|
|
447
|
+
if (!everConnected) return;
|
|
448
|
+
try {
|
|
449
|
+
var res = await fetch(BRIDGE + "/health", { signal: AbortSignal.timeout(1000) });
|
|
450
|
+
if (res.ok) {
|
|
451
|
+
var data = await res.json();
|
|
452
|
+
if (data && data.stats && data.stats.memoryMb !== undefined && ramEl) {
|
|
453
|
+
ramEl.textContent = data.stats.memoryMb.toFixed(1);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
} catch(e) {}
|
|
457
|
+
}
|
|
458
|
+
setInterval(fetchServerStats, 4000);
|
|
459
|
+
|
|
460
|
+
function triggerManualReindex() {
|
|
461
|
+
if (!everConnected) {
|
|
462
|
+
log("Cannot index: Server is offline", "err");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (reindexBtnLabel) reindexBtnLabel.textContent = "Indexing…";
|
|
466
|
+
log("⚡ Manual re-index triggered…", "info");
|
|
467
|
+
parent.postMessage({ pluginMessage: { type: "manual-reindex" } }, "*");
|
|
468
|
+
}
|
|
419
469
|
|
|
420
470
|
var READ_OPS = [
|
|
421
471
|
"get_selection","get_design","get_page_nodes","screenshot",
|
|
@@ -918,6 +968,20 @@
|
|
|
918
968
|
return;
|
|
919
969
|
}
|
|
920
970
|
|
|
971
|
+
if (msg.type === "node-diff") {
|
|
972
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
973
|
+
try { ws.send(JSON.stringify({ type: "node-diff", nodes: msg.nodes })); } catch(e) {}
|
|
974
|
+
}
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (msg.type === "index-chunk") {
|
|
979
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
980
|
+
try { ws.send(JSON.stringify({ type: "index-chunk", nodes: msg.nodes })); } catch(e) {}
|
|
981
|
+
}
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
|
|
921
985
|
if (msg.type === "document-change") {
|
|
922
986
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
923
987
|
try { ws.send(JSON.stringify({ type: "document-change" })); } catch(e) {}
|
|
@@ -926,6 +990,7 @@
|
|
|
926
990
|
}
|
|
927
991
|
|
|
928
992
|
if (msg.type === "index-update") {
|
|
993
|
+
if (reindexBtnLabel) reindexBtnLabel.textContent = "⚡ Indexed";
|
|
929
994
|
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
930
995
|
try {
|
|
931
996
|
ws.send(JSON.stringify({
|
|
@@ -935,13 +1000,21 @@
|
|
|
935
1000
|
sessionId: msg.sessionId,
|
|
936
1001
|
startMs: msg.startMs
|
|
937
1002
|
}));
|
|
938
|
-
|
|
1003
|
+
var nodeCount = (msg.data && msg.data.pageNodes) ? msg.data.pageNodes.length : 0;
|
|
1004
|
+
log("⚡ Indexed " + (msg.fileName || "file") + " (" + (Date.now() - msg.startMs) + "ms)", "ok");
|
|
939
1005
|
} catch(e) {}
|
|
940
1006
|
}
|
|
1007
|
+
setTimeout(function() {
|
|
1008
|
+
if (reindexBtnLabel) reindexBtnLabel.textContent = "⚡ Index";
|
|
1009
|
+
}, 3000);
|
|
941
1010
|
return;
|
|
942
1011
|
}
|
|
943
1012
|
|
|
944
1013
|
var isRead = READ_OPS.indexOf(msg.operation) >= 0;
|
|
1014
|
+
var durationStr = msg.durationMs !== undefined ? " (" + msg.durationMs + "ms)" : "";
|
|
1015
|
+
if (msg.durationMs !== undefined && speedEl) {
|
|
1016
|
+
speedEl.textContent = msg.durationMs;
|
|
1017
|
+
}
|
|
945
1018
|
if (msg.success) {
|
|
946
1019
|
if (isRead) {
|
|
947
1020
|
readCount++;
|
|
@@ -950,11 +1023,11 @@
|
|
|
950
1023
|
writeCount++;
|
|
951
1024
|
document.getElementById("write-count").textContent = writeCount;
|
|
952
1025
|
}
|
|
953
|
-
log("✓ " + (msg.id ? msg.id.slice(-6) : "ok"), "ok");
|
|
1026
|
+
log("✓ " + (msg.id ? msg.id.slice(-6) : "ok") + durationStr, "ok");
|
|
954
1027
|
} else {
|
|
955
1028
|
errCount++;
|
|
956
1029
|
document.getElementById("err-count").textContent = errCount;
|
|
957
|
-
log("✗ " + (msg.id ? msg.id.slice(-6) : "err") + ": " + (msg.error || "fail"), "err");
|
|
1030
|
+
log("✗ " + (msg.id ? msg.id.slice(-6) : "err") + durationStr + ": " + (msg.error || "fail"), "err");
|
|
958
1031
|
}
|
|
959
1032
|
var payloadJson = msg.dataJson !== undefined
|
|
960
1033
|
? msg.dataJson
|
|
@@ -964,11 +1037,15 @@
|
|
|
964
1037
|
|
|
965
1038
|
initConnection();
|
|
966
1039
|
|
|
967
|
-
// Smooth Drag Resize
|
|
1040
|
+
// Smooth Drag Resize (60FPS via requestAnimationFrame)
|
|
968
1041
|
(function initResize() {
|
|
969
1042
|
var grip = document.getElementById("resize-grip");
|
|
970
1043
|
if (!grip) return;
|
|
971
1044
|
|
|
1045
|
+
var rafId = null;
|
|
1046
|
+
var lastW = 0, lastH = 0;
|
|
1047
|
+
var saveTimer = null;
|
|
1048
|
+
|
|
972
1049
|
grip.addEventListener("pointerdown", function(e) {
|
|
973
1050
|
e.preventDefault();
|
|
974
1051
|
grip.setPointerCapture(e.pointerId);
|
|
@@ -980,13 +1057,29 @@
|
|
|
980
1057
|
function onPointerMove(ev) {
|
|
981
1058
|
var nw = Math.max(280, Math.min(1000, Math.round(startW + (ev.clientX - startX))));
|
|
982
1059
|
var nh = Math.max(220, Math.min(1200, Math.round(startH + (ev.clientY - startY))));
|
|
983
|
-
|
|
1060
|
+
lastW = nw;
|
|
1061
|
+
lastH = nh;
|
|
1062
|
+
|
|
1063
|
+
if (!rafId) {
|
|
1064
|
+
rafId = requestAnimationFrame(function() {
|
|
1065
|
+
parent.postMessage({ pluginMessage: { type: "resize", width: lastW, height: lastH } }, "*");
|
|
1066
|
+
rafId = null;
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
984
1069
|
}
|
|
985
1070
|
|
|
986
1071
|
function onPointerUp(ev) {
|
|
987
1072
|
try { grip.releasePointerCapture(ev.pointerId); } catch(err) {}
|
|
988
1073
|
window.removeEventListener("pointermove", onPointerMove);
|
|
989
1074
|
window.removeEventListener("pointerup", onPointerUp);
|
|
1075
|
+
if (rafId) {
|
|
1076
|
+
cancelAnimationFrame(rafId);
|
|
1077
|
+
rafId = null;
|
|
1078
|
+
}
|
|
1079
|
+
if (lastW > 0 && lastH > 0) {
|
|
1080
|
+
parent.postMessage({ pluginMessage: { type: "resize", width: lastW, height: lastH } }, "*");
|
|
1081
|
+
parent.postMessage({ pluginMessage: { type: "save-window-size", width: lastW, height: lastH } }, "*");
|
|
1082
|
+
}
|
|
990
1083
|
}
|
|
991
1084
|
|
|
992
1085
|
window.addEventListener("pointermove", onPointerMove);
|