@pedroab0/skills 1.0.1 → 1.2.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/README.md +19 -3
- package/bin/cli.js +175 -35
- package/figma-extractor/README.md +14 -6
- package/figma-extractor/SKILL.md +18 -16
- package/figma-extractor/figma-plugin/README.md +10 -5
- package/figma-extractor/figma-plugin/code.js +461 -87
- package/figma-extractor/figma-plugin/ui.html +76 -23
- package/figma-extractor/scripts/fetch_figma.js +465 -61
- package/figma-node-builder/README.md +1 -1
- package/figma-node-builder/SKILL.md +7 -8
- package/figma-node-builder/scripts/inspect_spec.js +15 -2
- package/figma-to-code/README.md +10 -4
- package/figma-to-code/SKILL.md +18 -16
- package/figma-to-code/scripts/fetch_figma.js +448 -53
- package/package.json +12 -3
- package/figma-extractor/demo/desktop_home.json +0 -2434
- package/figma-extractor/demo/dt_money_desktop_home.png +0 -0
- package/figma-extractor/demo/dt_money_preview.png +0 -0
- package/figma-extractor/demo/dt_money_selection.json +0 -472
- package/figma-extractor/demo/index.html +0 -188
- package/figma-extractor/demo/style.css +0 -360
- package/figma-extractor/figma-plugin/assets/cover-1920.png +0 -0
- package/figma-extractor/figma-plugin/assets/cover.jpg +0 -0
- package/figma-extractor/figma-plugin/assets/cover.png +0 -0
- package/figma-extractor/figma-plugin/assets/icon-128.png +0 -0
- package/figma-extractor/figma-plugin/assets/icon.png +0 -0
- package/figma-extractor/scripts/test_fetch_figma.js +0 -601
- package/figma-node-builder/scripts/test_inspect_spec.js +0 -148
- package/figma-to-code/scripts/test_fetch_figma.js +0 -581
|
@@ -261,14 +261,72 @@
|
|
|
261
261
|
setTimeout(() => URL.revokeObjectURL(url), 5000);
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
function flashButton(btn, text) {
|
|
265
|
-
|
|
264
|
+
function flashButton(btn, text, bgOverride = '') {
|
|
265
|
+
if (!btn.dataset.originalHtml) {
|
|
266
|
+
btn.dataset.originalHtml = btn.innerHTML;
|
|
267
|
+
}
|
|
268
|
+
if (btn._flashTimer) {
|
|
269
|
+
clearTimeout(btn._flashTimer);
|
|
270
|
+
}
|
|
266
271
|
btn.innerHTML = `<span>${text}</span>`;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
272
|
+
if (bgOverride) {
|
|
273
|
+
btn.style.background = bgOverride;
|
|
274
|
+
} else {
|
|
275
|
+
btn.style.borderColor = '#10b981';
|
|
276
|
+
}
|
|
277
|
+
btn._flashTimer = setTimeout(() => {
|
|
278
|
+
btn.innerHTML = btn.dataset.originalHtml;
|
|
279
|
+
btn.style.background = '';
|
|
270
280
|
btn.style.borderColor = '';
|
|
271
|
-
|
|
281
|
+
btn._flashTimer = null;
|
|
282
|
+
}, 1800);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Figma plugin UI runs inside a null-origin sandboxed iframe where navigator.clipboard
|
|
286
|
+
// is blocked by Permissions-Policy. Synchronous execCommand('copy') is the official
|
|
287
|
+
// cross-browser / Figma Desktop clipboard mechanism, with Async Clipboard as fallback.
|
|
288
|
+
function copyUsingExecCommand(text) {
|
|
289
|
+
const textarea = document.createElement('textarea');
|
|
290
|
+
textarea.value = text;
|
|
291
|
+
textarea.setAttribute('readonly', '');
|
|
292
|
+
textarea.style.position = 'fixed';
|
|
293
|
+
textarea.style.top = '0';
|
|
294
|
+
textarea.style.left = '0';
|
|
295
|
+
textarea.style.width = '1px';
|
|
296
|
+
textarea.style.height = '1px';
|
|
297
|
+
textarea.style.padding = '0';
|
|
298
|
+
textarea.style.border = 'none';
|
|
299
|
+
textarea.style.outline = 'none';
|
|
300
|
+
textarea.style.boxShadow = 'none';
|
|
301
|
+
textarea.style.background = 'transparent';
|
|
302
|
+
textarea.style.opacity = '0';
|
|
303
|
+
textarea.style.pointerEvents = 'none';
|
|
304
|
+
document.body.appendChild(textarea);
|
|
305
|
+
textarea.focus({ preventScroll: true });
|
|
306
|
+
textarea.select();
|
|
307
|
+
textarea.setSelectionRange(0, textarea.value.length);
|
|
308
|
+
let copied = false;
|
|
309
|
+
try {
|
|
310
|
+
copied = document.execCommand('copy');
|
|
311
|
+
} catch (_) {
|
|
312
|
+
copied = false;
|
|
313
|
+
} finally {
|
|
314
|
+
document.body.removeChild(textarea);
|
|
315
|
+
}
|
|
316
|
+
return copied;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function copyTextToClipboard(text) {
|
|
320
|
+
if (copyUsingExecCommand(text)) {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
324
|
+
try {
|
|
325
|
+
await navigator.clipboard.writeText(text);
|
|
326
|
+
return true;
|
|
327
|
+
} catch (_) {}
|
|
328
|
+
}
|
|
329
|
+
return false;
|
|
272
330
|
}
|
|
273
331
|
|
|
274
332
|
// Pure vanilla zero-dependency ZIP archive creator (Store format)
|
|
@@ -369,7 +427,7 @@
|
|
|
369
427
|
document.getElementById('node-bounds').textContent = `${w} × ${h} px`;
|
|
370
428
|
document.getElementById('hint-name').textContent = msg.name || 'component';
|
|
371
429
|
|
|
372
|
-
const jsonStr = JSON.stringify(msg.spec || {}
|
|
430
|
+
const jsonStr = JSON.stringify(msg.spec || {});
|
|
373
431
|
const sizeKb = (new Blob([jsonStr]).size / 1024).toFixed(1);
|
|
374
432
|
document.getElementById('node-size').textContent = `${sizeKb} KB`;
|
|
375
433
|
|
|
@@ -414,30 +472,25 @@
|
|
|
414
472
|
}
|
|
415
473
|
};
|
|
416
474
|
|
|
417
|
-
// Copy to clipboard
|
|
475
|
+
// Copy to clipboard (compact pruned JSON matching CLI default)
|
|
418
476
|
document.getElementById('btn-copy').onclick = async () => {
|
|
419
477
|
if (!currentData) return;
|
|
420
|
-
const jsonStr = JSON.stringify(currentData.spec
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
btn.innerHTML = '<span>✔ Copied Node JSON!</span>';
|
|
426
|
-
btn.style.background = '#16a34a';
|
|
478
|
+
const jsonStr = JSON.stringify(currentData.spec);
|
|
479
|
+
const btn = document.getElementById('btn-copy');
|
|
480
|
+
const ok = await copyTextToClipboard(jsonStr);
|
|
481
|
+
if (ok) {
|
|
482
|
+
flashButton(btn, '✔ Copied Node JSON!', '#16a34a');
|
|
427
483
|
parent.postMessage({ pluginMessage: { type: 'NOTIFY', message: `Copied "${currentData.name}.json" to clipboard!` } }, '*');
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}, 2000);
|
|
432
|
-
} catch (err) {
|
|
433
|
-
console.error('Clipboard failed', err);
|
|
484
|
+
} else {
|
|
485
|
+
flashButton(btn, '✖ Copy failed — use .json', '#dc2626');
|
|
486
|
+
parent.postMessage({ pluginMessage: { type: 'NOTIFY', message: 'Failed to access clipboard. Please download .json instead.', error: true } }, '*');
|
|
434
487
|
}
|
|
435
488
|
};
|
|
436
489
|
|
|
437
490
|
// Download JSON
|
|
438
491
|
document.getElementById('btn-download-json').onclick = () => {
|
|
439
492
|
if (!currentData) return;
|
|
440
|
-
const jsonStr = JSON.stringify(currentData.spec
|
|
493
|
+
const jsonStr = JSON.stringify(currentData.spec);
|
|
441
494
|
const blob = new Blob([jsonStr], { type: 'application/json' });
|
|
442
495
|
triggerDownload(blob, `${currentData.name}.json`);
|
|
443
496
|
flashButton(document.getElementById('btn-download-json'), '✔ Saved .json');
|
|
@@ -453,7 +506,7 @@
|
|
|
453
506
|
// Download Both (.specs.zip)
|
|
454
507
|
document.getElementById('btn-download-both').onclick = () => {
|
|
455
508
|
if (!currentData) return;
|
|
456
|
-
const jsonStr = JSON.stringify(currentData.spec
|
|
509
|
+
const jsonStr = JSON.stringify(currentData.spec);
|
|
457
510
|
const enc = new TextEncoder();
|
|
458
511
|
const files = [
|
|
459
512
|
{ name: `${currentData.name}.json`, data: enc.encode(jsonStr) }
|