@pedroab0/skills 1.1.0 → 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/bin/cli.js +23 -3
- 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 +285 -35
- package/figma-to-code/scripts/fetch_figma.js +285 -35
- package/package.json +7 -2
- 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 -687
- package/figma-node-builder/scripts/test_inspect_spec.js +0 -163
- package/figma-to-code/scripts/test_fetch_figma.js +0 -660
|
@@ -551,13 +551,234 @@ function normalizeComponentProps(componentProperties) {
|
|
|
551
551
|
// AST Pruning & Token Optimization
|
|
552
552
|
// -----------------------------------------------------------------------------
|
|
553
553
|
|
|
554
|
-
|
|
554
|
+
const VECTOR_PRIMITIVE_TYPES = new Set([
|
|
555
|
+
'VECTOR',
|
|
556
|
+
'BOOLEAN_OPERATION',
|
|
557
|
+
'STAR',
|
|
558
|
+
'LINE',
|
|
559
|
+
'REGULAR_POLYGON',
|
|
560
|
+
'POLYGON',
|
|
561
|
+
]);
|
|
562
|
+
|
|
563
|
+
function cleanLayerName(rawName, fallback = 'Node') {
|
|
564
|
+
if (!rawName || typeof rawName !== 'string') return fallback;
|
|
565
|
+
const trimmed = rawName.trim();
|
|
566
|
+
if (!trimmed) return fallback;
|
|
567
|
+
if (/^data:image\//i.test(trimmed)) return 'Image';
|
|
568
|
+
if (/^(?:<svg\b|<\?xml\b)/i.test(trimmed)) return 'SVG';
|
|
569
|
+
if (/^https?:\/\//i.test(trimmed) && trimmed.length > 60) {
|
|
570
|
+
try {
|
|
571
|
+
const lastSeg = trimmed.split('?')[0].split('/').pop();
|
|
572
|
+
return lastSeg && lastSeg.length <= 60 ? lastSeg : 'Image';
|
|
573
|
+
} catch (_) {
|
|
574
|
+
return 'Image';
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const singleLine = trimmed.replace(/\s+/g, ' ');
|
|
578
|
+
return singleLine.length > 80 ? singleLine.slice(0, 80) + '…' : singleLine;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function isRawAssetString(str) {
|
|
582
|
+
if (!str || typeof str !== 'string') return false;
|
|
583
|
+
const t = str.trim();
|
|
584
|
+
return /^data:image\//i.test(t) || /^(?:<svg\b|<\?xml\b)/i.test(t);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function nodeHasImageFill(node) {
|
|
588
|
+
return Array.isArray(node.fills) && node.fills.some((f) => f && f.visible !== false && f.type === 'IMAGE');
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const GRAPHIC_NAME_RE =
|
|
592
|
+
/svg|icon|ícone|icone|logo|marca|brand|illustrat|ilustra|vector|vetor|symbol|simbol|símbolo|glyph|graphic|ornament|pattern|artwork/i;
|
|
593
|
+
|
|
594
|
+
function hasSvgExportSetting(node) {
|
|
595
|
+
if (!Array.isArray(node.exportSettings) || node.exportSettings.length === 0) return false;
|
|
596
|
+
return node.exportSettings.some((s) => s && String(s.format || '').toUpperCase() === 'SVG');
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function subtreeHasNoInstances(node, maxNodes = 400) {
|
|
600
|
+
let visited = 0;
|
|
601
|
+
function walk(curr, isRoot) {
|
|
602
|
+
if (!curr || typeof curr !== 'object' || ++visited > maxNodes) return false;
|
|
603
|
+
const t = curr.type || '';
|
|
604
|
+
if (!isRoot && (t === 'INSTANCE' || t === 'COMPONENT' || t === 'COMPONENT_SET')) return false;
|
|
605
|
+
const kids = Array.isArray(curr.children) ? curr.children.filter((c) => c && c.visible !== false) : [];
|
|
606
|
+
for (const k of kids) {
|
|
607
|
+
if (!walk(k, false)) return false;
|
|
608
|
+
}
|
|
609
|
+
return true;
|
|
610
|
+
}
|
|
611
|
+
return walk(node, true);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function analyzeVectorSubtree(node, varMap, maxNodes = 600) {
|
|
615
|
+
let visited = 0;
|
|
616
|
+
let vectorPrimitiveCount = 0;
|
|
617
|
+
let shapeCount = 0;
|
|
618
|
+
let plainRectCount = 0;
|
|
619
|
+
let primaryFill = null;
|
|
620
|
+
|
|
621
|
+
const rootType = node.type || '';
|
|
622
|
+
const hasSvgExport = hasSvgExportSetting(node);
|
|
623
|
+
|
|
624
|
+
// Preserve Auto-Layout wrapper frames with their own styling (e.g., Icon Badge) unless explicitly exported as SVG
|
|
625
|
+
if (rootType === 'FRAME' && !hasSvgExport && node.layoutMode && node.layoutMode !== 'NONE') {
|
|
626
|
+
const ownFills = simplifyFills(node.fills, node.boundVariables || {}, varMap);
|
|
627
|
+
const ownStrokes = simplifyStrokes(node.strokes, node.strokeWeight, node.strokeAlign, node.boundVariables || {}, varMap);
|
|
628
|
+
if (ownFills || ownStrokes || node.cornerRadius) {
|
|
629
|
+
return { isPure: false, primaryFill: null };
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function check(curr, isRoot) {
|
|
634
|
+
if (!curr || typeof curr !== 'object' || ++visited > maxNodes) return false;
|
|
635
|
+
const t = curr.type || '';
|
|
636
|
+
if (t === 'TEXT' || t === 'SLICE' || t === 'STICKY' || t === 'TABLE') return false;
|
|
637
|
+
if (!isRoot && (t === 'INSTANCE' || t === 'COMPONENT' || t === 'COMPONENT_SET')) return false;
|
|
638
|
+
if (nodeHasImageFill(curr)) return false;
|
|
639
|
+
|
|
640
|
+
if (!primaryFill) {
|
|
641
|
+
const fills = simplifyFills(curr.fills, curr.boundVariables || {}, varMap);
|
|
642
|
+
const solidFill = fills && fills.find((f) => f.type === 'SOLID' && f.color);
|
|
643
|
+
if (solidFill) {
|
|
644
|
+
primaryFill = solidFill;
|
|
645
|
+
} else {
|
|
646
|
+
const strokes = simplifyStrokes(curr.strokes, curr.strokeWeight, curr.strokeAlign, curr.boundVariables || {}, varMap);
|
|
647
|
+
if (strokes && strokes.colors && strokes.colors[0]) {
|
|
648
|
+
const c0 = strokes.colors[0];
|
|
649
|
+
primaryFill = typeof c0 === 'object' ? { type: 'SOLID', ...c0 } : { type: 'SOLID', color: c0 };
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (VECTOR_PRIMITIVE_TYPES.has(t)) {
|
|
655
|
+
vectorPrimitiveCount++;
|
|
656
|
+
return true;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (t === 'ELLIPSE') {
|
|
660
|
+
shapeCount++;
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (t === 'RECTANGLE') {
|
|
665
|
+
if (curr.isMask === true || /clip|mask|vector|path|rect|bound|bg/i.test(String(curr.name || ''))) {
|
|
666
|
+
shapeCount++;
|
|
667
|
+
} else {
|
|
668
|
+
plainRectCount++;
|
|
669
|
+
}
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (t === 'GROUP' || t === 'FRAME' || (isRoot && (t === 'INSTANCE' || t === 'COMPONENT'))) {
|
|
674
|
+
if (!isRoot && t === 'FRAME' && curr.layoutMode && curr.layoutMode !== 'NONE') {
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
const kids = Array.isArray(curr.children) ? curr.children.filter((c) => c && c.visible !== false) : [];
|
|
678
|
+
if (kids.length === 0) return !isRoot;
|
|
679
|
+
for (const k of kids) {
|
|
680
|
+
if (!check(k, false)) return false;
|
|
681
|
+
}
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const treeOk = check(node, true);
|
|
689
|
+
const w = node.absoluteBoundingBox ? node.absoluteBoundingBox.width : 9999;
|
|
690
|
+
const h = node.absoluteBoundingBox ? node.absoluteBoundingBox.height : 9999;
|
|
691
|
+
|
|
692
|
+
if (!treeOk) {
|
|
693
|
+
if (hasSvgExport && w <= 800 && h <= 800 && !nodeHasImageFill(node) && subtreeHasNoInstances(node)) {
|
|
694
|
+
return { isPure: true, primaryFill };
|
|
695
|
+
}
|
|
696
|
+
return { isPure: false, primaryFill: null };
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const isSmallGraphic = w <= 160 && h <= 160;
|
|
700
|
+
const hasGraphicName = GRAPHIC_NAME_RE.test(String(node.name || ''));
|
|
701
|
+
|
|
702
|
+
const hasEnoughPrimitives =
|
|
703
|
+
vectorPrimitiveCount > 0 ||
|
|
704
|
+
(shapeCount + plainRectCount >= 2 && (hasSvgExport || (isSmallGraphic && hasGraphicName)));
|
|
705
|
+
|
|
706
|
+
const rectGuardOk =
|
|
707
|
+
plainRectCount === 0 ||
|
|
708
|
+
vectorPrimitiveCount >= 2 ||
|
|
709
|
+
isSmallGraphic ||
|
|
710
|
+
hasGraphicName ||
|
|
711
|
+
hasSvgExport;
|
|
712
|
+
|
|
713
|
+
const isPure = hasEnoughPrimitives && rectGuardOk;
|
|
714
|
+
return { isPure, primaryFill };
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function analyzeMaskedImageSubtree(node) {
|
|
718
|
+
const t = node.type || '';
|
|
719
|
+
if (t !== 'GROUP' && t !== 'FRAME') return null;
|
|
720
|
+
if (node.layoutMode && node.layoutMode !== 'NONE') return null;
|
|
721
|
+
|
|
722
|
+
const kids = Array.isArray(node.children) ? node.children.filter((c) => c && c.visible !== false) : [];
|
|
723
|
+
if (kids.length === 0 || kids.length > 20) return null;
|
|
724
|
+
|
|
725
|
+
let foundImageFill = false;
|
|
726
|
+
let hasMaskOrClip = false;
|
|
727
|
+
|
|
728
|
+
function scan(curr) {
|
|
729
|
+
const ct = curr.type || '';
|
|
730
|
+
if (ct === 'TEXT' || ct === 'INSTANCE' || ct === 'COMPONENT') return false;
|
|
731
|
+
if (curr.isMask === true) hasMaskOrClip = true;
|
|
732
|
+
if (nodeHasImageFill(curr)) foundImageFill = true;
|
|
733
|
+
|
|
734
|
+
const sub = Array.isArray(curr.children) ? curr.children.filter((c) => c && c.visible !== false) : [];
|
|
735
|
+
for (const s of sub) {
|
|
736
|
+
if (!scan(s)) return false;
|
|
737
|
+
}
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (!scan(node) || !foundImageFill) return null;
|
|
742
|
+
if (!hasMaskOrClip && !/mask|image|img|photo|avatar|banner|thumb/i.test(String(node.name || ''))) {
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function pruneFigmaNode(node, context = {}, options = {}, _depth = 0) {
|
|
555
749
|
if (!node || typeof node !== 'object') return node;
|
|
556
750
|
|
|
557
751
|
const { varMap, components, componentSets } = context;
|
|
752
|
+
const cleanName = cleanLayerName(node.name, 'Node');
|
|
753
|
+
const bound = node.boundVariables || {};
|
|
754
|
+
const bounds = node.absoluteBoundingBox
|
|
755
|
+
? {
|
|
756
|
+
width: Math.round(node.absoluteBoundingBox.width),
|
|
757
|
+
height: Math.round(node.absoluteBoundingBox.height),
|
|
758
|
+
}
|
|
759
|
+
: undefined;
|
|
760
|
+
|
|
761
|
+
// 1. Leaf Vector Primitives (VECTOR, BOOLEAN_OPERATION, STAR, LINE, POLYGON)
|
|
762
|
+
if (VECTOR_PRIMITIVE_TYPES.has(node.type)) {
|
|
763
|
+
const vecNode = { name: cleanName, type: 'VECTOR' };
|
|
764
|
+
if (options.includeIds && node.id) vecNode.id = node.id;
|
|
765
|
+
if (node.visible === false) vecNode.visible = false;
|
|
766
|
+
if (bounds) vecNode.bounds = bounds;
|
|
767
|
+
const fills = simplifyFills(node.fills, bound, varMap);
|
|
768
|
+
if (fills) {
|
|
769
|
+
vecNode.fills = fills;
|
|
770
|
+
} else {
|
|
771
|
+
const strokes = simplifyStrokes(node.strokes, node.strokeWeight, node.strokeAlign, bound, varMap);
|
|
772
|
+
if (strokes && strokes.colors && strokes.colors[0]) {
|
|
773
|
+
const c0 = strokes.colors[0];
|
|
774
|
+
vecNode.fills = [typeof c0 === 'object' ? { type: 'SOLID', ...c0 } : { type: 'SOLID', color: c0 }];
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return vecNode;
|
|
778
|
+
}
|
|
558
779
|
|
|
559
780
|
const pruned = {
|
|
560
|
-
name:
|
|
781
|
+
name: cleanName,
|
|
561
782
|
type: node.type,
|
|
562
783
|
};
|
|
563
784
|
|
|
@@ -567,16 +788,14 @@ function pruneFigmaNode(node, context = {}, options = {}) {
|
|
|
567
788
|
|
|
568
789
|
if (node.visible === false) pruned.visible = false;
|
|
569
790
|
|
|
570
|
-
const bound = node.boundVariables || {};
|
|
571
|
-
|
|
572
791
|
// Component Instance & Master Resolution
|
|
573
792
|
if (node.type === 'INSTANCE') {
|
|
574
793
|
if (node.componentId && components && components[node.componentId]) {
|
|
575
794
|
const master = components[node.componentId];
|
|
576
795
|
const set = master.componentSetId && componentSets ? componentSets[master.componentSetId] : null;
|
|
577
796
|
pruned.component = {
|
|
578
|
-
name: set ? set.name : master.name,
|
|
579
|
-
variant: master.name,
|
|
797
|
+
name: cleanLayerName(set ? set.name : master.name, cleanName),
|
|
798
|
+
variant: cleanLayerName(master.name, cleanName),
|
|
580
799
|
};
|
|
581
800
|
if (options.includeIds) {
|
|
582
801
|
pruned.component.id = node.componentId;
|
|
@@ -588,12 +807,7 @@ function pruneFigmaNode(node, context = {}, options = {}) {
|
|
|
588
807
|
|
|
589
808
|
// Shallow instances: skip internal rendering sub-layers if requested
|
|
590
809
|
if (options.shallowInstances) {
|
|
591
|
-
if (
|
|
592
|
-
pruned.bounds = {
|
|
593
|
-
width: Math.round(node.absoluteBoundingBox.width),
|
|
594
|
-
height: Math.round(node.absoluteBoundingBox.height),
|
|
595
|
-
};
|
|
596
|
-
}
|
|
810
|
+
if (bounds) pruned.bounds = bounds;
|
|
597
811
|
return pruned;
|
|
598
812
|
}
|
|
599
813
|
} else if (node.type === 'COMPONENT' || node.type === 'COMPONENT_SET') {
|
|
@@ -601,11 +815,42 @@ function pruneFigmaNode(node, context = {}, options = {}) {
|
|
|
601
815
|
if (props) pruned.props = props;
|
|
602
816
|
}
|
|
603
817
|
|
|
818
|
+
// 2. Collapse Pure SVG / Icon / Vector Graphic Containers
|
|
819
|
+
if (_depth > 0 || node.type === 'GROUP' || node.type === 'INSTANCE') {
|
|
820
|
+
const vecCheck = analyzeVectorSubtree(node, varMap);
|
|
821
|
+
if (vecCheck.isPure) {
|
|
822
|
+
if (node.type !== 'INSTANCE' && node.type !== 'COMPONENT') {
|
|
823
|
+
pruned.type = 'VECTOR';
|
|
824
|
+
pruned.isSvg = true;
|
|
825
|
+
}
|
|
826
|
+
if (bounds) pruned.bounds = bounds;
|
|
827
|
+
const ownFills = simplifyFills(node.fills, bound, varMap);
|
|
828
|
+
if (ownFills) {
|
|
829
|
+
pruned.fills = ownFills;
|
|
830
|
+
} else if (vecCheck.primaryFill) {
|
|
831
|
+
pruned.fills = [vecCheck.primaryFill];
|
|
832
|
+
}
|
|
833
|
+
return pruned;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// 3. Collapse Masked Image Groups
|
|
838
|
+
if (_depth > 0 && analyzeMaskedImageSubtree(node)) {
|
|
839
|
+
if (bounds) pruned.bounds = bounds;
|
|
840
|
+
pruned.fills = [{ type: 'IMAGE' }];
|
|
841
|
+
if (node.cornerRadius) pruned.cornerRadius = node.cornerRadius;
|
|
842
|
+
return pruned;
|
|
843
|
+
}
|
|
844
|
+
|
|
604
845
|
// Layout & Flexbox properties
|
|
605
846
|
if (node.layoutMode && node.layoutMode !== 'NONE') {
|
|
606
847
|
pruned.layoutMode = node.layoutMode;
|
|
607
|
-
if (node.primaryAxisAlignItems
|
|
608
|
-
|
|
848
|
+
if (node.primaryAxisAlignItems && node.primaryAxisAlignItems !== 'MIN') {
|
|
849
|
+
pruned.primaryAxisAlignItems = node.primaryAxisAlignItems;
|
|
850
|
+
}
|
|
851
|
+
if (node.counterAxisAlignItems && node.counterAxisAlignItems !== 'MIN') {
|
|
852
|
+
pruned.counterAxisAlignItems = node.counterAxisAlignItems;
|
|
853
|
+
}
|
|
609
854
|
|
|
610
855
|
if (node.itemSpacing) {
|
|
611
856
|
pruned.itemSpacing = node.itemSpacing;
|
|
@@ -626,18 +871,15 @@ function pruneFigmaNode(node, context = {}, options = {}) {
|
|
|
626
871
|
if (padToken) pruned.paddingToken = padToken;
|
|
627
872
|
}
|
|
628
873
|
|
|
629
|
-
if (node.layoutWrap) pruned.layoutWrap = node.layoutWrap;
|
|
874
|
+
if (node.layoutWrap && node.layoutWrap !== 'NO_WRAP') pruned.layoutWrap = node.layoutWrap;
|
|
630
875
|
}
|
|
631
876
|
|
|
632
877
|
if (node.layoutGrow) pruned.layoutGrow = node.layoutGrow;
|
|
633
|
-
if (node.layoutAlign) pruned.layoutAlign = node.layoutAlign;
|
|
878
|
+
if (node.layoutAlign && node.layoutAlign !== 'INHERIT') pruned.layoutAlign = node.layoutAlign;
|
|
634
879
|
|
|
635
880
|
// Dimensions & Coordinates
|
|
636
|
-
if (
|
|
637
|
-
pruned.bounds =
|
|
638
|
-
width: Math.round(node.absoluteBoundingBox.width),
|
|
639
|
-
height: Math.round(node.absoluteBoundingBox.height),
|
|
640
|
-
};
|
|
881
|
+
if (bounds) {
|
|
882
|
+
pruned.bounds = bounds;
|
|
641
883
|
}
|
|
642
884
|
|
|
643
885
|
if (node.minWidth) pruned.minWidth = node.minWidth;
|
|
@@ -656,8 +898,9 @@ function pruneFigmaNode(node, context = {}, options = {}) {
|
|
|
656
898
|
pruned.cornerRadius = node.cornerRadius;
|
|
657
899
|
const radiusToken = resolveVariableToken(bound.cornerRadius, varMap);
|
|
658
900
|
if (radiusToken) pruned.cornerRadiusToken = radiusToken;
|
|
901
|
+
} else if (Array.isArray(node.rectangleCornerRadii) && node.rectangleCornerRadii.some((r) => r > 0)) {
|
|
902
|
+
pruned.cornerRadii = node.rectangleCornerRadii;
|
|
659
903
|
}
|
|
660
|
-
if (node.rectangleCornerRadii) pruned.cornerRadii = node.rectangleCornerRadii;
|
|
661
904
|
if (node.opacity !== undefined && node.opacity !== 1) pruned.opacity = node.opacity;
|
|
662
905
|
|
|
663
906
|
// Effects (Drop Shadows, Layer Blurs)
|
|
@@ -675,25 +918,32 @@ function pruneFigmaNode(node, context = {}, options = {}) {
|
|
|
675
918
|
|
|
676
919
|
// Typography (for TEXT nodes)
|
|
677
920
|
if (node.type === 'TEXT') {
|
|
678
|
-
pruned.characters = node.characters;
|
|
921
|
+
pruned.characters = isRawAssetString(node.characters) ? '[raw-asset-omitted]' : node.characters;
|
|
679
922
|
if (node.style) {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
923
|
+
const typo = {};
|
|
924
|
+
if (node.style.fontFamily) typo.fontFamily = node.style.fontFamily;
|
|
925
|
+
if (node.style.fontSize) typo.fontSize = node.style.fontSize;
|
|
926
|
+
if (node.style.fontWeight) typo.fontWeight = node.style.fontWeight;
|
|
927
|
+
if (node.style.lineHeightPx) typo.lineHeightPx = Math.round(node.style.lineHeightPx);
|
|
928
|
+
if (node.style.letterSpacing) typo.letterSpacing = node.style.letterSpacing;
|
|
929
|
+
if (node.style.textAlignHorizontal && node.style.textAlignHorizontal !== 'LEFT') {
|
|
930
|
+
typo.textAlignHorizontal = node.style.textAlignHorizontal;
|
|
931
|
+
}
|
|
932
|
+
if (node.style.textAlignVertical && node.style.textAlignVertical !== 'TOP') {
|
|
933
|
+
typo.textAlignVertical = node.style.textAlignVertical;
|
|
934
|
+
}
|
|
935
|
+
if (Object.keys(typo).length > 0) {
|
|
936
|
+
pruned.typography = typo;
|
|
937
|
+
}
|
|
689
938
|
}
|
|
690
939
|
}
|
|
691
940
|
|
|
692
941
|
// Recursively process children
|
|
693
942
|
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
.map((c) => pruneFigmaNode(c, context, options));
|
|
943
|
+
const visibleChildren = node.children.filter((c) => c && c.visible !== false);
|
|
944
|
+
if (visibleChildren.length > 0) {
|
|
945
|
+
pruned.children = visibleChildren.map((c) => pruneFigmaNode(c, context, options, _depth + 1));
|
|
946
|
+
}
|
|
697
947
|
}
|
|
698
948
|
|
|
699
949
|
return pruned;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pedroab0/skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Curated collection of modular, production-ready skills for AI coding agents (Claude Code, Cursor, Kiro, OpenCode, Antigravity)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"skills": "bin/cli.js",
|
|
@@ -41,6 +41,11 @@
|
|
|
41
41
|
"figma-extractor",
|
|
42
42
|
"figma-node-builder",
|
|
43
43
|
"README.md",
|
|
44
|
-
"LICENSE"
|
|
44
|
+
"LICENSE",
|
|
45
|
+
"!**/demo/**",
|
|
46
|
+
"!**/demos/**",
|
|
47
|
+
"!**/.specs/**",
|
|
48
|
+
"!**/test_*.js",
|
|
49
|
+
"!**/figma-plugin/assets/**"
|
|
45
50
|
]
|
|
46
51
|
}
|