@promptui-lib/figma-parser 0.1.19 → 0.1.20

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.
@@ -1,10 +1,10 @@
1
- export { parseLayout, parseSizing, parseLayoutAndSizing } from './layout-parser.js';
1
+ export { parseLayout, parseSizing, parseLayoutAndSizing, parsePadding } from './layout-parser.js';
2
2
  export type { ILayoutProperties } from './layout-parser.js';
3
3
  export { parseStyles, parseBackgroundColor, parseBorder, parseBorderRadius, parseBoxShadow } from './style-parser.js';
4
4
  export type { IStyleProperties } from './style-parser.js';
5
5
  export { parseTextStyles, extractTextContent } from './text-parser.js';
6
6
  export type { ITextProperties } from './text-parser.js';
7
- export { parsePosition, parseContainerPosition, hasAutoLayout } from './position-parser.js';
7
+ export { parsePosition, parseContainerPosition, hasAutoLayout, isAbsolutelyPositioned } from './position-parser.js';
8
8
  export type { IPositionProperties } from './position-parser.js';
9
9
  export { parseNode, parseNodes, parseFrameName } from './node-parser.js';
10
10
  export type { IParseOptions } from './node-parser.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACtH,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC5F,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClG,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACtH,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACpH,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -1,6 +1,6 @@
1
- export { parseLayout, parseSizing, parseLayoutAndSizing } from './layout-parser.js';
1
+ export { parseLayout, parseSizing, parseLayoutAndSizing, parsePadding } from './layout-parser.js';
2
2
  export { parseStyles, parseBackgroundColor, parseBorder, parseBorderRadius, parseBoxShadow } from './style-parser.js';
3
3
  export { parseTextStyles, extractTextContent } from './text-parser.js';
4
- export { parsePosition, parseContainerPosition, hasAutoLayout } from './position-parser.js';
4
+ export { parsePosition, parseContainerPosition, hasAutoLayout, isAbsolutelyPositioned } from './position-parser.js';
5
5
  export { parseNode, parseNodes, parseFrameName } from './node-parser.js';
6
6
  export { detectSemanticTag, normalizeName, getSemanticAttributes } from './semantic-detector.js';
@@ -15,16 +15,22 @@ export interface ILayoutProperties {
15
15
  flex?: IStyleProperty;
16
16
  flexWrap?: IStyleProperty;
17
17
  }
18
+ /**
19
+ * Parseia padding de um node (funciona com ou sem Auto Layout)
20
+ */
21
+ export declare function parsePadding(node: IFigmaNode): IStyleProperty | null;
18
22
  /**
19
23
  * Parseia AutoLayout de um node
20
24
  */
21
25
  export declare function parseLayout(node: IFigmaNode): ILayoutProperties;
22
26
  /**
23
27
  * Parseia sizing de um node
28
+ * @param isRoot - indica se é o node raiz do componente
24
29
  */
25
- export declare function parseSizing(node: IFigmaNode): ILayoutProperties;
30
+ export declare function parseSizing(node: IFigmaNode, isRoot?: boolean): ILayoutProperties;
26
31
  /**
27
32
  * Combina layout e sizing
33
+ * @param isRoot - indica se é o node raiz do componente
28
34
  */
29
- export declare function parseLayoutAndSizing(node: IFigmaNode): IStyleProperty[];
35
+ export declare function parseLayoutAndSizing(node: IFigmaNode, isRoot?: boolean): IStyleProperty[];
30
36
  //# sourceMappingURL=layout-parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout-parser.d.ts","sourceRoot":"","sources":["../../src/parser/layout-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA4BrE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,iBAAiB,CA2F/D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAoE/D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,EAAE,CASvE"}
1
+ {"version":3,"file":"layout-parser.d.ts","sourceRoot":"","sources":["../../src/parser/layout-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA4BrE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAuBpE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,iBAAiB,CA2F/D;AAwBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,iBAAiB,CA+ExF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,cAAc,EAAE,CAShG"}
@@ -26,6 +26,24 @@ function formatPixels(value) {
26
26
  }
27
27
  return `${rounded}px`;
28
28
  }
29
+ /**
30
+ * Parseia padding de um node (funciona com ou sem Auto Layout)
31
+ */
32
+ export function parsePadding(node) {
33
+ const hasPadding = node.paddingTop ||
34
+ node.paddingRight ||
35
+ node.paddingBottom ||
36
+ node.paddingLeft;
37
+ if (!hasPadding) {
38
+ return null;
39
+ }
40
+ const paddingToken = generatePaddingToken(node.paddingTop ?? 0, node.paddingRight ?? 0, node.paddingBottom ?? 0, node.paddingLeft ?? 0);
41
+ return {
42
+ property: 'padding',
43
+ value: paddingToken,
44
+ token: paddingToken.startsWith('$') ? paddingToken : undefined,
45
+ };
46
+ }
29
47
  /**
30
48
  * Parseia AutoLayout de um node
31
49
  */
@@ -102,11 +120,32 @@ export function parseLayout(node) {
102
120
  }
103
121
  return properties;
104
122
  }
123
+ /**
124
+ * Detecta se o node é uma tela/página (root component que deve ocupar viewport)
125
+ */
126
+ function isScreenNode(node) {
127
+ const name = node.name.toLowerCase().replace(/^#/, '');
128
+ const screenPatterns = /screen|page|view|layout|template|tela|pagina/i;
129
+ // Verifica se é uma tela pelo nome
130
+ if (screenPatterns.test(name)) {
131
+ return true;
132
+ }
133
+ // Verifica se tem dimensões típicas de tela (>= 320px width e >= 568px height)
134
+ const box = node.absoluteBoundingBox;
135
+ if (box && box.width >= 320 && box.height >= 568) {
136
+ // Se é um frame de nível superior com dimensões de tela
137
+ return true;
138
+ }
139
+ return false;
140
+ }
105
141
  /**
106
142
  * Parseia sizing de um node
143
+ * @param isRoot - indica se é o node raiz do componente
107
144
  */
108
- export function parseSizing(node) {
145
+ export function parseSizing(node, isRoot = false) {
109
146
  const properties = {};
147
+ // Se é o node raiz e é uma tela, usa viewport units
148
+ const isScreen = isRoot && isScreenNode(node);
110
149
  // Width
111
150
  if (node.layoutSizingHorizontal) {
112
151
  switch (node.layoutSizingHorizontal) {
@@ -126,17 +165,17 @@ export function parseSizing(node) {
126
165
  if (node.absoluteBoundingBox?.width) {
127
166
  properties.width = {
128
167
  property: 'width',
129
- value: formatPixels(node.absoluteBoundingBox.width),
168
+ value: isScreen ? '100vw' : formatPixels(node.absoluteBoundingBox.width),
130
169
  };
131
170
  }
132
171
  break;
133
172
  }
134
173
  }
135
174
  else if (node.absoluteBoundingBox?.width) {
136
- // Fallback: Se não tem layoutSizing mas tem dimensões, usa o tamanho absoluto
175
+ // Fallback: Se não tem layoutSizing mas tem dimensões
137
176
  properties.width = {
138
177
  property: 'width',
139
- value: formatPixels(node.absoluteBoundingBox.width),
178
+ value: isScreen ? '100vw' : formatPixels(node.absoluteBoundingBox.width),
140
179
  };
141
180
  }
142
181
  // Height
@@ -158,27 +197,35 @@ export function parseSizing(node) {
158
197
  if (node.absoluteBoundingBox?.height) {
159
198
  properties.height = {
160
199
  property: 'height',
161
- value: formatPixels(node.absoluteBoundingBox.height),
200
+ value: isScreen ? '100vh' : formatPixels(node.absoluteBoundingBox.height),
162
201
  };
163
202
  }
164
203
  break;
165
204
  }
166
205
  }
167
206
  else if (node.absoluteBoundingBox?.height) {
168
- // Fallback: Se não tem layoutSizing mas tem dimensões, usa o tamanho absoluto
207
+ // Fallback: Se não tem layoutSizing mas tem dimensões
169
208
  properties.height = {
170
209
  property: 'height',
171
- value: formatPixels(node.absoluteBoundingBox.height),
210
+ value: isScreen ? '100vh' : formatPixels(node.absoluteBoundingBox.height),
211
+ };
212
+ }
213
+ // Para screens, adiciona overflow hidden para evitar scroll
214
+ if (isScreen) {
215
+ properties.flex = {
216
+ property: 'overflow',
217
+ value: 'hidden',
172
218
  };
173
219
  }
174
220
  return properties;
175
221
  }
176
222
  /**
177
223
  * Combina layout e sizing
224
+ * @param isRoot - indica se é o node raiz do componente
178
225
  */
179
- export function parseLayoutAndSizing(node) {
226
+ export function parseLayoutAndSizing(node, isRoot = false) {
180
227
  const layout = parseLayout(node);
181
- const sizing = parseSizing(node);
228
+ const sizing = parseSizing(node, isRoot);
182
229
  const combined = { ...layout, ...sizing };
183
230
  return Object.values(combined).filter((prop) => prop !== undefined);
184
231
  }
@@ -1 +1 @@
1
- {"version":3,"file":"node-parser.d.ts","sourceRoot":"","sources":["../../src/parser/node-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EAMb,cAAc,EACd,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAiB5B,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAwB7D;AAuND;;GAEG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CA6Cf;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE,aAAkB,GAC1B,aAAa,EAAE,CAEjB"}
1
+ {"version":3,"file":"node-parser.d.ts","sourceRoot":"","sources":["../../src/parser/node-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EAOb,cAAc,EACd,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAiB5B,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAwB7D;AA+RD;;GAEG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,aAAkB,GAC1B,aAAa,CA6Cf;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,GAAE,aAAkB,GAC1B,aAAa,EAAE,CAEjB"}
@@ -3,11 +3,11 @@
3
3
  * Parser principal que converte node do Figma em AST do componente
4
4
  */
5
5
  import { generateComponentName, generateFileName, generateBEMBlock, generateBEMElement, classifyComponent, extractBaseName, extractVariants, isComponentNode, } from '@promptui-lib/core';
6
- import { parseLayoutAndSizing, parseSizing } from './layout-parser.js';
6
+ import { parseLayoutAndSizing, parseSizing, parsePadding } from './layout-parser.js';
7
7
  import { parseStyles } from './style-parser.js';
8
8
  import { parseTextStyles } from './text-parser.js';
9
9
  import { detectSemanticTag, normalizeName, getSemanticAttributes } from './semantic-detector.js';
10
- import { parsePosition, parseContainerPosition } from './position-parser.js';
10
+ import { parsePosition, parseContainerPosition, hasAutoLayout } from './position-parser.js';
11
11
  /**
12
12
  * Parseia o nome do frame para extrair informações
13
13
  */
@@ -32,6 +32,44 @@ export function parseFrameName(name) {
32
32
  extractedProps,
33
33
  };
34
34
  }
35
+ /**
36
+ * Verifica se um node deve ser exportado como asset (SVG/imagem)
37
+ * Tipos que são gráficos vetoriais ou ícones devem ser exportados como SVG
38
+ */
39
+ function isAssetNode(node) {
40
+ // Tipos de nodes que são gráficos vetoriais
41
+ const vectorTypes = ['VECTOR', 'BOOLEAN_OPERATION', 'STAR', 'LINE', 'ELLIPSE', 'REGULAR_POLYGON'];
42
+ if (vectorTypes.includes(node.type)) {
43
+ return true;
44
+ }
45
+ // Verifica se o nome indica que é um ícone
46
+ const name = node.name.toLowerCase();
47
+ if (/^(icon|ico|icone|svg)[-_]?/i.test(name) || name.includes('-icon') || name.includes('_icon')) {
48
+ return true;
49
+ }
50
+ // Frames pequenos (< 64x64) com fills complexos podem ser ícones
51
+ if (node.type === 'FRAME' && node.absoluteBoundingBox) {
52
+ const { width, height } = node.absoluteBoundingBox;
53
+ if (width <= 64 && height <= 64) {
54
+ // Verifica se tem apenas elementos vetoriais como filhos
55
+ const hasOnlyVectors = node.children?.every((child) => vectorTypes.includes(child.type) || child.type === 'GROUP');
56
+ if (hasOnlyVectors && node.children && node.children.length > 0) {
57
+ return true;
58
+ }
59
+ }
60
+ }
61
+ return false;
62
+ }
63
+ /**
64
+ * Gera nome seguro para arquivo de asset
65
+ */
66
+ function generateAssetFileName(nodeName, blockName) {
67
+ const cleanName = nodeName
68
+ .toLowerCase()
69
+ .replace(/[^a-z0-9]+/g, '-')
70
+ .replace(/^-+|-+$/g, '');
71
+ return `${blockName}-${cleanName || 'icon'}`;
72
+ }
35
73
  /**
36
74
  * Converte um node em elemento JSX
37
75
  * Se o filho for um componente (marcado com #), retorna IJSXComponent em vez de JSX inline
@@ -74,6 +112,22 @@ function nodeToJSX(node, blockName, depth = 0, parentNode, usedTags) {
74
112
  fileName: childFileName,
75
113
  });
76
114
  }
115
+ else if (isAssetNode(child)) {
116
+ // Se é um asset (ícone/vetor), cria referência para exportação
117
+ const childNormalizedName = normalizeName(child.name);
118
+ const childElementName = childNormalizedName.replace(/\s+/g, '-');
119
+ const childClassName = generateBEMElement(blockName, childElementName);
120
+ children.push({
121
+ type: 'asset',
122
+ nodeId: child.id,
123
+ fileName: generateAssetFileName(child.name, blockName),
124
+ format: 'svg',
125
+ className: childClassName,
126
+ alt: childNormalizedName,
127
+ width: child.absoluteBoundingBox?.width,
128
+ height: child.absoluteBoundingBox?.height,
129
+ });
130
+ }
77
131
  else {
78
132
  children.push(nodeToJSX(child, blockName, depth + 1, node, tagsSet));
79
133
  }
@@ -110,12 +164,20 @@ function collectStyles(node, blockName, depth = 0, parentNode) {
110
164
  // Para GROUP: extrai sizing e position: relative (container sem flex)
111
165
  // Para outros tipos (RECTANGLE, etc): extrai apenas sizing (width, height)
112
166
  if (node.type === 'FRAME' || node.type === 'COMPONENT' || node.type === 'INSTANCE') {
113
- properties.push(...parseLayoutAndSizing(node));
167
+ properties.push(...parseLayoutAndSizing(node, isRoot));
114
168
  // Se é um container sem Auto Layout, adiciona position: relative
115
169
  const containerPos = parseContainerPosition(node);
116
170
  if (containerPos) {
117
171
  properties.push(containerPos);
118
172
  }
173
+ // Extrai padding mesmo se não tem Auto Layout (para elementos como inputs)
174
+ // parseLayoutAndSizing já extrai padding quando tem Auto Layout, mas não quando não tem
175
+ if (!hasAutoLayout(node)) {
176
+ const paddingProp = parsePadding(node);
177
+ if (paddingProp) {
178
+ properties.push(paddingProp);
179
+ }
180
+ }
119
181
  }
120
182
  else if (node.type === 'GROUP') {
121
183
  // GROUPs são containers visuais - precisam de position: relative e sizing
@@ -16,6 +16,11 @@ export interface IPositionProperties {
16
16
  * Verifica se um node pai tem Auto Layout
17
17
  */
18
18
  export declare function hasAutoLayout(node: IFigmaNode): boolean;
19
+ /**
20
+ * Verifica se um node está posicionado absolutamente
21
+ * (mesmo dentro de um container com Auto Layout)
22
+ */
23
+ export declare function isAbsolutelyPositioned(node: IFigmaNode): boolean;
19
24
  /**
20
25
  * Parseia constraints e posição de um node
21
26
  */
@@ -1 +1 @@
1
- {"version":3,"file":"position-parser.d.ts","sourceRoot":"","sources":["../../src/parser/position-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAgB,MAAM,oBAAoB,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B;AAoBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAEvD;AAwBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,UAAU,CAAC,EAAE,UAAU,GACtB,cAAc,EAAE,CAmIlB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAM/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAQ9E"}
1
+ {"version":3,"file":"position-parser.d.ts","sourceRoot":"","sources":["../../src/parser/position-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAgB,MAAM,oBAAoB,CAAC;AAEnF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,cAAc,CAAC;CAC5B;AAoBD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAEvD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAEhE;AAwBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,UAAU,CAAC,EAAE,UAAU,GACtB,cAAc,EAAE,CA6IlB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAa/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,GAAG,IAAI,CAQ9E"}
@@ -25,6 +25,13 @@ function formatPixels(value) {
25
25
  export function hasAutoLayout(node) {
26
26
  return node.layoutMode !== undefined && node.layoutMode !== 'NONE';
27
27
  }
28
+ /**
29
+ * Verifica se um node está posicionado absolutamente
30
+ * (mesmo dentro de um container com Auto Layout)
31
+ */
32
+ export function isAbsolutelyPositioned(node) {
33
+ return node.layoutPositioning === 'ABSOLUTE';
34
+ }
28
35
  /**
29
36
  * Calcula a posição relativa de um filho em relação ao pai
30
37
  */
@@ -46,8 +53,16 @@ function calculateRelativePosition(child, parent) {
46
53
  */
47
54
  export function parsePosition(node, parentNode) {
48
55
  const properties = [];
49
- // Se não tem pai ou o pai tem Auto Layout, não precisa de posicionamento absoluto
50
- if (!parentNode || hasAutoLayout(parentNode)) {
56
+ if (!parentNode) {
57
+ return properties;
58
+ }
59
+ // Verifica se o node precisa de posicionamento absoluto:
60
+ // 1. O pai NÃO tem Auto Layout, OU
61
+ // 2. O node tem layoutPositioning: 'ABSOLUTE' (absoluto dentro de Auto Layout)
62
+ const parentHasAutoLayout = hasAutoLayout(parentNode);
63
+ const nodeIsAbsolute = isAbsolutelyPositioned(node);
64
+ if (parentHasAutoLayout && !nodeIsAbsolute) {
65
+ // Pai tem Auto Layout e o node não é absoluto - não precisa de posicionamento
51
66
  return properties;
52
67
  }
53
68
  // Calcula posição relativa
@@ -56,7 +71,7 @@ export function parsePosition(node, parentNode) {
56
71
  return properties;
57
72
  }
58
73
  const constraints = node.constraints;
59
- // Se é um elemento dentro de um container sem Auto Layout, usa posição absoluta
74
+ // Elemento precisa de posição absoluta
60
75
  properties.push({
61
76
  property: 'position',
62
77
  value: 'absolute',
@@ -176,11 +191,16 @@ export function parsePosition(node, parentNode) {
176
191
  * (necessário quando tem filhos com position: absolute)
177
192
  */
178
193
  export function needsRelativePosition(node) {
179
- // Se não tem Auto Layout e tem filhos, precisa de relative
180
- if (!hasAutoLayout(node) && node.children && node.children.length > 0) {
194
+ if (!node.children || node.children.length === 0) {
195
+ return false;
196
+ }
197
+ // Se não tem Auto Layout, precisa de relative (todos os filhos serão absolutos)
198
+ if (!hasAutoLayout(node)) {
181
199
  return true;
182
200
  }
183
- return false;
201
+ // Se tem Auto Layout, verifica se algum filho tem layoutPositioning: 'ABSOLUTE'
202
+ const hasAbsoluteChild = node.children.some((child) => isAbsolutelyPositioned(child));
203
+ return hasAbsoluteChild;
184
204
  }
185
205
  /**
186
206
  * Parseia position: relative para containers sem Auto Layout
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptui-lib/figma-parser",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "private": false,
5
5
  "description": "Figma API client and parser for PromptUI",
6
6
  "license": "UNLICENSED",
@@ -30,7 +30,7 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "@promptui-lib/core": "0.1.19"
33
+ "@promptui-lib/core": "0.1.20"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^20.0.0",