@promptui-lib/figma-parser 0.1.7 → 0.1.8
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/dist/parser/index.d.ts +1 -0
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +1 -0
- package/dist/parser/node-parser.d.ts.map +1 -1
- package/dist/parser/node-parser.js +19 -11
- package/dist/parser/semantic-detector.d.ts +20 -0
- package/dist/parser/semantic-detector.d.ts.map +1 -0
- package/dist/parser/semantic-detector.js +664 -0
- package/package.json +2 -2
package/dist/parser/index.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ export { parseTextStyles, extractTextContent } from './text-parser.js';
|
|
|
6
6
|
export type { ITextProperties } from './text-parser.js';
|
|
7
7
|
export { parseNode, parseNodes, parseFrameName } from './node-parser.js';
|
|
8
8
|
export type { IParseOptions } from './node-parser.js';
|
|
9
|
+
export { detectSemanticTag, normalizeName, getSemanticAttributes } from './semantic-detector.js';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,MAAM,kBAAkB,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,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,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"}
|
package/dist/parser/index.js
CHANGED
|
@@ -2,3 +2,4 @@ export { parseLayout, parseSizing, parseLayoutAndSizing } from './layout-parser.
|
|
|
2
2
|
export { parseStyles, parseBackgroundColor, parseBorder, parseBorderRadius, parseBoxShadow } from './style-parser.js';
|
|
3
3
|
export { parseTextStyles, extractTextContent } from './text-parser.js';
|
|
4
4
|
export { parseNode, parseNodes, parseFrameName } from './node-parser.js';
|
|
5
|
+
export { detectSemanticTag, normalizeName, getSemanticAttributes } from './semantic-detector.js';
|
|
@@ -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,EAKb,cAAc,EACd,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;
|
|
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,EAKb,cAAc,EACd,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAe5B,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;AAuJD;;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"}
|
|
@@ -6,6 +6,7 @@ import { generateComponentName, generateFileName, generateBEMBlock, generateBEME
|
|
|
6
6
|
import { parseLayoutAndSizing } from './layout-parser.js';
|
|
7
7
|
import { parseStyles } from './style-parser.js';
|
|
8
8
|
import { parseTextStyles } from './text-parser.js';
|
|
9
|
+
import { detectSemanticTag, normalizeName, getSemanticAttributes } from './semantic-detector.js';
|
|
9
10
|
/**
|
|
10
11
|
* Parseia o nome do frame para extrair informações
|
|
11
12
|
*/
|
|
@@ -33,17 +34,18 @@ export function parseFrameName(name) {
|
|
|
33
34
|
/**
|
|
34
35
|
* Converte um node em elemento JSX
|
|
35
36
|
*/
|
|
36
|
-
function nodeToJSX(node, blockName, depth = 0) {
|
|
37
|
+
function nodeToJSX(node, blockName, depth = 0, parentNode) {
|
|
37
38
|
const isRoot = depth === 0;
|
|
38
|
-
|
|
39
|
+
// Normaliza o nome (traduz PT->EN)
|
|
40
|
+
const normalizedName = normalizeName(node.name);
|
|
41
|
+
const elementName = isRoot ? '' : normalizedName.replace(/\s+/g, '-');
|
|
39
42
|
const className = isRoot
|
|
40
43
|
? blockName
|
|
41
44
|
: generateBEMElement(blockName, elementName);
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
45
|
+
// Detecta a tag HTML semântica apropriada
|
|
46
|
+
const tag = detectSemanticTag(node, parentNode, depth);
|
|
47
|
+
// Obtém atributos semânticos (href, type, etc.)
|
|
48
|
+
const semanticAttrs = getSemanticAttributes(node, tag, normalizedName);
|
|
47
49
|
// Processa filhos
|
|
48
50
|
const children = [];
|
|
49
51
|
if (node.type === 'TEXT' && node.characters) {
|
|
@@ -55,15 +57,19 @@ function nodeToJSX(node, blockName, depth = 0) {
|
|
|
55
57
|
if (child.visible === false || child.name.startsWith('_')) {
|
|
56
58
|
continue;
|
|
57
59
|
}
|
|
58
|
-
children.push(nodeToJSX(child, blockName, depth + 1));
|
|
60
|
+
children.push(nodeToJSX(child, blockName, depth + 1, node));
|
|
59
61
|
}
|
|
60
62
|
}
|
|
63
|
+
// Tags self-closing
|
|
64
|
+
const selfClosingTags = ['img', 'input', 'br', 'hr'];
|
|
65
|
+
const isSelfClosing = selfClosingTags.includes(tag) ||
|
|
66
|
+
(children.length === 0 && node.type !== 'TEXT');
|
|
61
67
|
return {
|
|
62
68
|
tag,
|
|
63
69
|
className,
|
|
64
|
-
props:
|
|
70
|
+
props: semanticAttrs,
|
|
65
71
|
children,
|
|
66
|
-
selfClosing:
|
|
72
|
+
selfClosing: isSelfClosing,
|
|
67
73
|
};
|
|
68
74
|
}
|
|
69
75
|
/**
|
|
@@ -72,7 +78,9 @@ function nodeToJSX(node, blockName, depth = 0) {
|
|
|
72
78
|
function collectStyles(node, blockName, depth = 0) {
|
|
73
79
|
const blocks = [];
|
|
74
80
|
const isRoot = depth === 0;
|
|
75
|
-
|
|
81
|
+
// Usa nome normalizado (traduzido)
|
|
82
|
+
const normalizedName = normalizeName(node.name);
|
|
83
|
+
const elementName = isRoot ? '' : normalizedName.replace(/\s+/g, '-');
|
|
76
84
|
const selector = isRoot
|
|
77
85
|
? `.${blockName}`
|
|
78
86
|
: `.${generateBEMElement(blockName, elementName)}`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Detector
|
|
3
|
+
* Detecta a tag HTML5 semântica apropriada baseado no contexto do Figma
|
|
4
|
+
* Referência: https://developer.mozilla.org/pt-BR/docs/Web/HTML/Element
|
|
5
|
+
*/
|
|
6
|
+
import type { IFigmaNode } from '@promptui-lib/core';
|
|
7
|
+
/**
|
|
8
|
+
* Normaliza um nome traduzindo termos comuns
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeName(name: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Detecta a tag HTML semântica apropriada
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectSemanticTag(node: IFigmaNode, parentNode?: IFigmaNode, depth?: number): string;
|
|
15
|
+
/**
|
|
16
|
+
* Gera atributos adicionais baseado no contexto
|
|
17
|
+
* Segue boas práticas de SEO e acessibilidade
|
|
18
|
+
*/
|
|
19
|
+
export declare function getSemanticAttributes(node: IFigmaNode, tag: string, normalizedName: string): Record<string, string>;
|
|
20
|
+
//# sourceMappingURL=semantic-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-detector.d.ts","sourceRoot":"","sources":["../../src/parser/semantic-detector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAmQrD;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAalD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,EAChB,UAAU,CAAC,EAAE,UAAU,EACvB,KAAK,GAAE,MAAU,GAChB,MAAM,CAmKR;AAqJD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,MAAM,GACrB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoKxB"}
|
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Detector
|
|
3
|
+
* Detecta a tag HTML5 semântica apropriada baseado no contexto do Figma
|
|
4
|
+
* Referência: https://developer.mozilla.org/pt-BR/docs/Web/HTML/Element
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Mapeamento de nomes comuns PT -> EN
|
|
8
|
+
*/
|
|
9
|
+
const NAME_TRANSLATIONS = {
|
|
10
|
+
// Navegação
|
|
11
|
+
'navegacao': 'navigation',
|
|
12
|
+
'menu': 'menu',
|
|
13
|
+
'nav': 'nav',
|
|
14
|
+
'breadcrumb': 'breadcrumb',
|
|
15
|
+
'migalhas': 'breadcrumb',
|
|
16
|
+
'abas': 'tabs',
|
|
17
|
+
'guias': 'tabs',
|
|
18
|
+
// Botões
|
|
19
|
+
'botao': 'button',
|
|
20
|
+
'btn': 'button',
|
|
21
|
+
'enviar': 'submit',
|
|
22
|
+
'cancelar': 'cancel',
|
|
23
|
+
'entrar': 'login',
|
|
24
|
+
'sair': 'logout',
|
|
25
|
+
'cadastrar': 'register',
|
|
26
|
+
'salvar': 'save',
|
|
27
|
+
'excluir': 'delete',
|
|
28
|
+
'editar': 'edit',
|
|
29
|
+
'fechar': 'close',
|
|
30
|
+
'voltar': 'back',
|
|
31
|
+
'proximo': 'next',
|
|
32
|
+
'anterior': 'previous',
|
|
33
|
+
'adicionar': 'add',
|
|
34
|
+
'remover': 'remove',
|
|
35
|
+
'copiar': 'copy',
|
|
36
|
+
'colar': 'paste',
|
|
37
|
+
'baixar': 'download',
|
|
38
|
+
'enviar-arquivo': 'upload',
|
|
39
|
+
// Formulários
|
|
40
|
+
'formulario': 'form',
|
|
41
|
+
'campo': 'field',
|
|
42
|
+
'entrada': 'input',
|
|
43
|
+
'senha': 'password',
|
|
44
|
+
'email': 'email',
|
|
45
|
+
'nome': 'name',
|
|
46
|
+
'telefone': 'phone',
|
|
47
|
+
'endereco': 'address',
|
|
48
|
+
'busca': 'search',
|
|
49
|
+
'pesquisa': 'search',
|
|
50
|
+
'filtro': 'filter',
|
|
51
|
+
'selecao': 'select',
|
|
52
|
+
'seletor': 'select',
|
|
53
|
+
'dropdown': 'select',
|
|
54
|
+
'checkbox': 'checkbox',
|
|
55
|
+
'caixa-selecao': 'checkbox',
|
|
56
|
+
'radio': 'radio',
|
|
57
|
+
'opcao': 'option',
|
|
58
|
+
'textarea': 'textarea',
|
|
59
|
+
'area-texto': 'textarea',
|
|
60
|
+
// Labels e textos
|
|
61
|
+
'rotulo': 'label',
|
|
62
|
+
'legenda': 'caption',
|
|
63
|
+
'descricao': 'description',
|
|
64
|
+
'dica': 'hint',
|
|
65
|
+
'erro': 'error',
|
|
66
|
+
'sucesso': 'success',
|
|
67
|
+
'aviso': 'warning',
|
|
68
|
+
'ajuda': 'help',
|
|
69
|
+
'info': 'info',
|
|
70
|
+
'obrigatorio': 'required',
|
|
71
|
+
// Títulos
|
|
72
|
+
'titulo': 'title',
|
|
73
|
+
'subtitulo': 'subtitle',
|
|
74
|
+
'cabecalho': 'header',
|
|
75
|
+
'rodape': 'footer',
|
|
76
|
+
'manchete': 'headline',
|
|
77
|
+
// Links
|
|
78
|
+
'link': 'link',
|
|
79
|
+
'ancora': 'anchor',
|
|
80
|
+
'esqueci': 'forgot',
|
|
81
|
+
'esqueci-minha-senha': 'forgot-password',
|
|
82
|
+
'esqueceu-senha': 'forgot-password',
|
|
83
|
+
'saiba-mais': 'learn-more',
|
|
84
|
+
'ver-mais': 'see-more',
|
|
85
|
+
'leia-mais': 'read-more',
|
|
86
|
+
// Seções
|
|
87
|
+
'secao': 'section',
|
|
88
|
+
'artigo': 'article',
|
|
89
|
+
'conteudo': 'content',
|
|
90
|
+
'principal': 'main',
|
|
91
|
+
'lateral': 'sidebar',
|
|
92
|
+
'barra-lateral': 'sidebar',
|
|
93
|
+
'complementar': 'aside',
|
|
94
|
+
// Cards e containers
|
|
95
|
+
'cartao': 'card',
|
|
96
|
+
'caixa': 'box',
|
|
97
|
+
'container': 'container',
|
|
98
|
+
'painel': 'panel',
|
|
99
|
+
'modal': 'dialog',
|
|
100
|
+
'dialogo': 'dialog',
|
|
101
|
+
'popup': 'dialog',
|
|
102
|
+
'gaveta': 'drawer',
|
|
103
|
+
'acordeao': 'accordion',
|
|
104
|
+
'expansivel': 'expandable',
|
|
105
|
+
'colapsavel': 'collapsible',
|
|
106
|
+
// Imagens e mídia
|
|
107
|
+
'imagem': 'image',
|
|
108
|
+
'foto': 'photo',
|
|
109
|
+
'icone': 'icon',
|
|
110
|
+
'logo': 'logo',
|
|
111
|
+
'avatar': 'avatar',
|
|
112
|
+
'miniatura': 'thumbnail',
|
|
113
|
+
'banner': 'banner',
|
|
114
|
+
'video': 'video',
|
|
115
|
+
'audio': 'audio',
|
|
116
|
+
'player': 'player',
|
|
117
|
+
'galeria': 'gallery',
|
|
118
|
+
'carrossel': 'carousel',
|
|
119
|
+
'slider': 'slider',
|
|
120
|
+
// Listas
|
|
121
|
+
'lista': 'list',
|
|
122
|
+
'item': 'item',
|
|
123
|
+
'grade': 'grid',
|
|
124
|
+
'tabela': 'table',
|
|
125
|
+
'linha': 'row',
|
|
126
|
+
'coluna': 'column',
|
|
127
|
+
'celula': 'cell',
|
|
128
|
+
// Feedback
|
|
129
|
+
'texto': 'text',
|
|
130
|
+
'paragrafo': 'paragraph',
|
|
131
|
+
'mensagem': 'message',
|
|
132
|
+
'notificacao': 'notification',
|
|
133
|
+
'alerta': 'alert',
|
|
134
|
+
'toast': 'toast',
|
|
135
|
+
'snackbar': 'snackbar',
|
|
136
|
+
'carregando': 'loading',
|
|
137
|
+
'progresso': 'progress',
|
|
138
|
+
'spinner': 'spinner',
|
|
139
|
+
'esqueleto': 'skeleton',
|
|
140
|
+
// Estados
|
|
141
|
+
'placeholder': 'placeholder',
|
|
142
|
+
'vazio': 'empty',
|
|
143
|
+
'erro-estado': 'error-state',
|
|
144
|
+
'desabilitado': 'disabled',
|
|
145
|
+
'ativo': 'active',
|
|
146
|
+
'selecionado': 'selected',
|
|
147
|
+
'focado': 'focused',
|
|
148
|
+
// Outros
|
|
149
|
+
'bem-vindo': 'welcome',
|
|
150
|
+
'bemvindo': 'welcome',
|
|
151
|
+
'acesse': 'access',
|
|
152
|
+
'sua-conta': 'your-account',
|
|
153
|
+
'minha-conta': 'my-account',
|
|
154
|
+
'perfil': 'profile',
|
|
155
|
+
'configuracoes': 'settings',
|
|
156
|
+
'preferencias': 'preferences',
|
|
157
|
+
'separador': 'divider',
|
|
158
|
+
'divisor': 'divider',
|
|
159
|
+
'espacador': 'spacer',
|
|
160
|
+
'badge': 'badge',
|
|
161
|
+
'etiqueta': 'tag',
|
|
162
|
+
'chip': 'chip',
|
|
163
|
+
'tooltip': 'tooltip',
|
|
164
|
+
'dica-ferramenta': 'tooltip',
|
|
165
|
+
'data': 'date',
|
|
166
|
+
'hora': 'time',
|
|
167
|
+
'preco': 'price',
|
|
168
|
+
'valor': 'value',
|
|
169
|
+
'quantidade': 'quantity',
|
|
170
|
+
'total': 'total',
|
|
171
|
+
'resumo': 'summary',
|
|
172
|
+
'detalhes': 'details',
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Patterns para detectar tipos de elementos
|
|
176
|
+
*/
|
|
177
|
+
const SEMANTIC_PATTERNS = {
|
|
178
|
+
// Títulos
|
|
179
|
+
heading: /^(title|heading|h[1-6]|titulo|cabecalho|header-text|headline)/i,
|
|
180
|
+
subheading: /^(subtitle|subheading|subtitulo|sub-title)/i,
|
|
181
|
+
// Botões
|
|
182
|
+
button: /^(btn|button|cta|submit|cancel|action|botao|icon-button)/i,
|
|
183
|
+
// Links
|
|
184
|
+
link: /(link|anchor|href|ancora|forgot|esqueci|saiba-mais|ver-mais|leia-mais)/i,
|
|
185
|
+
// Formulários
|
|
186
|
+
form: /^(form|formulario|login-form|signup-form|contact-form|search-form)/i,
|
|
187
|
+
input: /^(input|field|textfield|entrada|campo|text-input)/i,
|
|
188
|
+
label: /^(label|rotulo|field-label|input-label)/i,
|
|
189
|
+
select: /^(select|dropdown|picker|seletor|selecao|combobox)/i,
|
|
190
|
+
textarea: /^(textarea|text-area|area-texto|multiline)/i,
|
|
191
|
+
checkbox: /^(checkbox|check-box|caixa-selecao|toggle)/i,
|
|
192
|
+
radio: /^(radio|radio-button|opcao-radio)/i,
|
|
193
|
+
fieldset: /^(fieldset|field-group|grupo-campos)/i,
|
|
194
|
+
// Navegação
|
|
195
|
+
nav: /^(nav|navigation|navbar|menu|navegacao|breadcrumb|tabs|pagination)/i,
|
|
196
|
+
// Seções estruturais
|
|
197
|
+
header: /^(header|cabecalho|top-bar|topbar|app-bar)/i,
|
|
198
|
+
footer: /^(footer|rodape|bottom-bar|bottom-nav)/i,
|
|
199
|
+
main: /^(main|content|principal|conteudo|page-content)/i,
|
|
200
|
+
section: /^(section|secao|block|grupo)/i,
|
|
201
|
+
article: /^(article|artigo|post|card|blog-post|news)/i,
|
|
202
|
+
aside: /^(aside|sidebar|lateral|barra-lateral|complementar)/i,
|
|
203
|
+
// Imagens e mídia
|
|
204
|
+
image: /^(image|img|photo|picture|imagem|foto|thumbnail|banner)/i,
|
|
205
|
+
avatar: /^(avatar|profile-pic|user-image|foto-perfil)/i,
|
|
206
|
+
icon: /^(icon|icone|ico)/i,
|
|
207
|
+
figure: /^(figure|figura|media-container)/i,
|
|
208
|
+
video: /^(video|player|media)/i,
|
|
209
|
+
audio: /^(audio|sound|som)/i,
|
|
210
|
+
// Listas
|
|
211
|
+
list: /^(list|ul|ol|menu-items|lista|items)/i,
|
|
212
|
+
listItem: /^(item|li|list-item|menu-item|option)/i,
|
|
213
|
+
// Tabelas
|
|
214
|
+
table: /^(table|tabela|data-table|grid-table)/i,
|
|
215
|
+
tableHead: /^(thead|table-head|cabecalho-tabela)/i,
|
|
216
|
+
tableBody: /^(tbody|table-body|corpo-tabela)/i,
|
|
217
|
+
tableRow: /^(tr|row|linha|table-row)/i,
|
|
218
|
+
tableCell: /^(td|cell|celula|table-cell)/i,
|
|
219
|
+
tableHeader: /^(th|header-cell|celula-cabecalho)/i,
|
|
220
|
+
// Texto
|
|
221
|
+
paragraph: /^(paragraph|text|body|description|paragrafo|descricao|copy)/i,
|
|
222
|
+
blockquote: /^(quote|blockquote|citacao|testimonial)/i,
|
|
223
|
+
code: /^(code|codigo|snippet|pre)/i,
|
|
224
|
+
time: /^(time|date|data|hora|datetime|timestamp)/i,
|
|
225
|
+
address: /^(address|endereco|contact-info)/i,
|
|
226
|
+
// Interativos
|
|
227
|
+
dialog: /^(dialog|modal|popup|overlay|dialogo)/i,
|
|
228
|
+
details: /^(details|accordion|expandable|collapsible|detalhes)/i,
|
|
229
|
+
summary: /^(summary|accordion-header|resumo)/i,
|
|
230
|
+
progress: /^(progress|loading|progresso|carregando|loader)/i,
|
|
231
|
+
meter: /^(meter|gauge|medidor)/i,
|
|
232
|
+
// Outros
|
|
233
|
+
divider: /^(divider|separator|hr|divisor|separador)/i,
|
|
234
|
+
badge: /^(badge|tag|chip|etiqueta|label-badge)/i,
|
|
235
|
+
tooltip: /^(tooltip|hint|dica-ferramenta)/i,
|
|
236
|
+
alert: /^(alert|notification|toast|snackbar|alerta|notificacao)/i,
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Normaliza um nome traduzindo termos comuns
|
|
240
|
+
*/
|
|
241
|
+
export function normalizeName(name) {
|
|
242
|
+
// Remove prefixos especiais
|
|
243
|
+
let normalized = name.replace(/^[#_]/, '').toLowerCase();
|
|
244
|
+
// Divide em palavras
|
|
245
|
+
const words = normalized.split(/[-_\s]+/);
|
|
246
|
+
// Traduz cada palavra
|
|
247
|
+
const translatedWords = words.map(word => {
|
|
248
|
+
return NAME_TRANSLATIONS[word] || word;
|
|
249
|
+
});
|
|
250
|
+
return translatedWords.join('-');
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Detecta a tag HTML semântica apropriada
|
|
254
|
+
*/
|
|
255
|
+
export function detectSemanticTag(node, parentNode, depth = 0) {
|
|
256
|
+
const name = node.name.toLowerCase();
|
|
257
|
+
const normalizedName = normalizeName(name);
|
|
258
|
+
// Texto sempre usa tag apropriada baseada no contexto
|
|
259
|
+
if (node.type === 'TEXT') {
|
|
260
|
+
return detectTextTag(node, parentNode, normalizedName);
|
|
261
|
+
}
|
|
262
|
+
// Verifica patterns para outros tipos (ordem importa!)
|
|
263
|
+
// Botões
|
|
264
|
+
if (SEMANTIC_PATTERNS.button.test(normalizedName)) {
|
|
265
|
+
return 'button';
|
|
266
|
+
}
|
|
267
|
+
// Links
|
|
268
|
+
if (SEMANTIC_PATTERNS.link.test(normalizedName)) {
|
|
269
|
+
return 'a';
|
|
270
|
+
}
|
|
271
|
+
// Formulários
|
|
272
|
+
if (SEMANTIC_PATTERNS.form.test(normalizedName)) {
|
|
273
|
+
return 'form';
|
|
274
|
+
}
|
|
275
|
+
if (SEMANTIC_PATTERNS.fieldset.test(normalizedName)) {
|
|
276
|
+
return 'fieldset';
|
|
277
|
+
}
|
|
278
|
+
if (SEMANTIC_PATTERNS.select.test(normalizedName)) {
|
|
279
|
+
return 'select';
|
|
280
|
+
}
|
|
281
|
+
if (SEMANTIC_PATTERNS.textarea.test(normalizedName)) {
|
|
282
|
+
return 'textarea';
|
|
283
|
+
}
|
|
284
|
+
if (SEMANTIC_PATTERNS.checkbox.test(normalizedName)) {
|
|
285
|
+
return 'input'; // type será checkbox
|
|
286
|
+
}
|
|
287
|
+
if (SEMANTIC_PATTERNS.radio.test(normalizedName)) {
|
|
288
|
+
return 'input'; // type será radio
|
|
289
|
+
}
|
|
290
|
+
if (SEMANTIC_PATTERNS.input.test(normalizedName)) {
|
|
291
|
+
return hasInputChild(node) ? 'div' : 'input';
|
|
292
|
+
}
|
|
293
|
+
// Navegação
|
|
294
|
+
if (SEMANTIC_PATTERNS.nav.test(normalizedName)) {
|
|
295
|
+
return 'nav';
|
|
296
|
+
}
|
|
297
|
+
// Seções estruturais
|
|
298
|
+
if (SEMANTIC_PATTERNS.header.test(normalizedName)) {
|
|
299
|
+
return depth === 0 ? 'header' : 'header';
|
|
300
|
+
}
|
|
301
|
+
if (SEMANTIC_PATTERNS.footer.test(normalizedName)) {
|
|
302
|
+
return 'footer';
|
|
303
|
+
}
|
|
304
|
+
if (SEMANTIC_PATTERNS.main.test(normalizedName)) {
|
|
305
|
+
return 'main';
|
|
306
|
+
}
|
|
307
|
+
if (SEMANTIC_PATTERNS.section.test(normalizedName)) {
|
|
308
|
+
return 'section';
|
|
309
|
+
}
|
|
310
|
+
if (SEMANTIC_PATTERNS.article.test(normalizedName)) {
|
|
311
|
+
return 'article';
|
|
312
|
+
}
|
|
313
|
+
if (SEMANTIC_PATTERNS.aside.test(normalizedName)) {
|
|
314
|
+
return 'aside';
|
|
315
|
+
}
|
|
316
|
+
// Mídia
|
|
317
|
+
if (SEMANTIC_PATTERNS.figure.test(normalizedName)) {
|
|
318
|
+
return 'figure';
|
|
319
|
+
}
|
|
320
|
+
if (SEMANTIC_PATTERNS.video.test(normalizedName)) {
|
|
321
|
+
return 'video';
|
|
322
|
+
}
|
|
323
|
+
if (SEMANTIC_PATTERNS.audio.test(normalizedName)) {
|
|
324
|
+
return 'audio';
|
|
325
|
+
}
|
|
326
|
+
if (SEMANTIC_PATTERNS.avatar.test(normalizedName) || SEMANTIC_PATTERNS.image.test(normalizedName)) {
|
|
327
|
+
return 'img';
|
|
328
|
+
}
|
|
329
|
+
if (SEMANTIC_PATTERNS.icon.test(normalizedName)) {
|
|
330
|
+
return 'span'; // Icons geralmente são spans com classes ou SVG inline
|
|
331
|
+
}
|
|
332
|
+
// Tabelas
|
|
333
|
+
if (SEMANTIC_PATTERNS.table.test(normalizedName)) {
|
|
334
|
+
return 'table';
|
|
335
|
+
}
|
|
336
|
+
if (SEMANTIC_PATTERNS.tableHead.test(normalizedName)) {
|
|
337
|
+
return 'thead';
|
|
338
|
+
}
|
|
339
|
+
if (SEMANTIC_PATTERNS.tableBody.test(normalizedName)) {
|
|
340
|
+
return 'tbody';
|
|
341
|
+
}
|
|
342
|
+
if (SEMANTIC_PATTERNS.tableRow.test(normalizedName)) {
|
|
343
|
+
return 'tr';
|
|
344
|
+
}
|
|
345
|
+
if (SEMANTIC_PATTERNS.tableHeader.test(normalizedName)) {
|
|
346
|
+
return 'th';
|
|
347
|
+
}
|
|
348
|
+
if (SEMANTIC_PATTERNS.tableCell.test(normalizedName)) {
|
|
349
|
+
return 'td';
|
|
350
|
+
}
|
|
351
|
+
// Listas
|
|
352
|
+
if (SEMANTIC_PATTERNS.list.test(normalizedName)) {
|
|
353
|
+
return 'ul';
|
|
354
|
+
}
|
|
355
|
+
if (SEMANTIC_PATTERNS.listItem.test(normalizedName)) {
|
|
356
|
+
return 'li';
|
|
357
|
+
}
|
|
358
|
+
// Interativos
|
|
359
|
+
if (SEMANTIC_PATTERNS.dialog.test(normalizedName)) {
|
|
360
|
+
return 'dialog';
|
|
361
|
+
}
|
|
362
|
+
if (SEMANTIC_PATTERNS.details.test(normalizedName)) {
|
|
363
|
+
return 'details';
|
|
364
|
+
}
|
|
365
|
+
if (SEMANTIC_PATTERNS.summary.test(normalizedName)) {
|
|
366
|
+
return 'summary';
|
|
367
|
+
}
|
|
368
|
+
if (SEMANTIC_PATTERNS.progress.test(normalizedName)) {
|
|
369
|
+
return 'div'; // progress element é muito específico
|
|
370
|
+
}
|
|
371
|
+
// Outros
|
|
372
|
+
if (SEMANTIC_PATTERNS.divider.test(normalizedName)) {
|
|
373
|
+
return 'hr';
|
|
374
|
+
}
|
|
375
|
+
if (SEMANTIC_PATTERNS.alert.test(normalizedName)) {
|
|
376
|
+
return 'div'; // com role="alert"
|
|
377
|
+
}
|
|
378
|
+
// Default
|
|
379
|
+
return 'div';
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Detecta a tag apropriada para elementos de texto
|
|
383
|
+
*/
|
|
384
|
+
function detectTextTag(node, parentNode, normalizedName) {
|
|
385
|
+
const name = normalizedName || normalizeName(node.name);
|
|
386
|
+
const fontSize = node.style?.fontSize ?? 16;
|
|
387
|
+
const fontWeight = node.style?.fontWeight ?? 400;
|
|
388
|
+
// Títulos - detecta por nome ou por tamanho de fonte
|
|
389
|
+
if (SEMANTIC_PATTERNS.heading.test(name)) {
|
|
390
|
+
return getHeadingLevel(fontSize);
|
|
391
|
+
}
|
|
392
|
+
if (SEMANTIC_PATTERNS.subheading.test(name)) {
|
|
393
|
+
return 'h2';
|
|
394
|
+
}
|
|
395
|
+
// Se fonte é grande e bold, provavelmente é heading
|
|
396
|
+
if (fontSize >= 24 && fontWeight >= 600) {
|
|
397
|
+
return getHeadingLevel(fontSize);
|
|
398
|
+
}
|
|
399
|
+
// Labels (geralmente associados a inputs)
|
|
400
|
+
if (SEMANTIC_PATTERNS.label.test(name)) {
|
|
401
|
+
return 'label';
|
|
402
|
+
}
|
|
403
|
+
// Links
|
|
404
|
+
if (SEMANTIC_PATTERNS.link.test(name)) {
|
|
405
|
+
return 'a';
|
|
406
|
+
}
|
|
407
|
+
// Citações
|
|
408
|
+
if (SEMANTIC_PATTERNS.blockquote.test(name)) {
|
|
409
|
+
return 'blockquote';
|
|
410
|
+
}
|
|
411
|
+
// Código
|
|
412
|
+
if (SEMANTIC_PATTERNS.code.test(name)) {
|
|
413
|
+
return 'code';
|
|
414
|
+
}
|
|
415
|
+
// Data/hora
|
|
416
|
+
if (SEMANTIC_PATTERNS.time.test(name)) {
|
|
417
|
+
return 'time';
|
|
418
|
+
}
|
|
419
|
+
// Endereço
|
|
420
|
+
if (SEMANTIC_PATTERNS.address.test(name)) {
|
|
421
|
+
return 'address';
|
|
422
|
+
}
|
|
423
|
+
// Verifica contexto do pai
|
|
424
|
+
if (parentNode) {
|
|
425
|
+
const parentName = normalizeName(parentNode.name);
|
|
426
|
+
// Se está dentro de um botão
|
|
427
|
+
if (SEMANTIC_PATTERNS.button.test(parentName)) {
|
|
428
|
+
return 'span';
|
|
429
|
+
}
|
|
430
|
+
// Se está dentro de um link
|
|
431
|
+
if (SEMANTIC_PATTERNS.link.test(parentName)) {
|
|
432
|
+
return 'span';
|
|
433
|
+
}
|
|
434
|
+
// Se está dentro de um label
|
|
435
|
+
if (SEMANTIC_PATTERNS.label.test(parentName)) {
|
|
436
|
+
return 'span';
|
|
437
|
+
}
|
|
438
|
+
// Se está dentro de um heading
|
|
439
|
+
if (SEMANTIC_PATTERNS.heading.test(parentName)) {
|
|
440
|
+
return 'span';
|
|
441
|
+
}
|
|
442
|
+
// Se pai é um item de lista
|
|
443
|
+
if (SEMANTIC_PATTERNS.listItem.test(parentName)) {
|
|
444
|
+
return 'span';
|
|
445
|
+
}
|
|
446
|
+
// Se pai é uma célula de tabela
|
|
447
|
+
if (SEMANTIC_PATTERNS.tableCell.test(parentName) || SEMANTIC_PATTERNS.tableHeader.test(parentName)) {
|
|
448
|
+
return 'span';
|
|
449
|
+
}
|
|
450
|
+
// Se pai é um fieldset, pode ser a legend
|
|
451
|
+
if (SEMANTIC_PATTERNS.fieldset.test(parentName)) {
|
|
452
|
+
return 'legend';
|
|
453
|
+
}
|
|
454
|
+
// Se pai é figure, pode ser figcaption
|
|
455
|
+
if (SEMANTIC_PATTERNS.figure.test(parentName)) {
|
|
456
|
+
return 'figcaption';
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// Parágrafos e descrições
|
|
460
|
+
if (SEMANTIC_PATTERNS.paragraph.test(name)) {
|
|
461
|
+
return 'p';
|
|
462
|
+
}
|
|
463
|
+
// Texto genérico - decide baseado no tamanho
|
|
464
|
+
const textLength = node.characters?.length ?? 0;
|
|
465
|
+
// Texto longo (>100 chars) -> parágrafo
|
|
466
|
+
if (textLength > 100) {
|
|
467
|
+
return 'p';
|
|
468
|
+
}
|
|
469
|
+
// Texto médio com quebras de linha -> parágrafo
|
|
470
|
+
if (node.characters?.includes('\n')) {
|
|
471
|
+
return 'p';
|
|
472
|
+
}
|
|
473
|
+
return 'span';
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Determina o nível do heading baseado no tamanho da fonte
|
|
477
|
+
*/
|
|
478
|
+
function getHeadingLevel(fontSize) {
|
|
479
|
+
if (fontSize >= 36)
|
|
480
|
+
return 'h1';
|
|
481
|
+
if (fontSize >= 30)
|
|
482
|
+
return 'h2';
|
|
483
|
+
if (fontSize >= 24)
|
|
484
|
+
return 'h3';
|
|
485
|
+
if (fontSize >= 20)
|
|
486
|
+
return 'h4';
|
|
487
|
+
if (fontSize >= 18)
|
|
488
|
+
return 'h5';
|
|
489
|
+
return 'h6';
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Verifica se um node contém um input como filho
|
|
493
|
+
*/
|
|
494
|
+
function hasInputChild(node) {
|
|
495
|
+
if (!node.children)
|
|
496
|
+
return false;
|
|
497
|
+
return node.children.some(child => {
|
|
498
|
+
const childName = normalizeName(child.name);
|
|
499
|
+
return SEMANTIC_PATTERNS.input.test(childName) ||
|
|
500
|
+
child.name.toLowerCase().includes('input');
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Gera atributos adicionais baseado no contexto
|
|
505
|
+
* Segue boas práticas de SEO e acessibilidade
|
|
506
|
+
*/
|
|
507
|
+
export function getSemanticAttributes(node, tag, normalizedName) {
|
|
508
|
+
const attrs = {};
|
|
509
|
+
// Links - SEO: links externos devem ter rel="noopener noreferrer"
|
|
510
|
+
if (tag === 'a') {
|
|
511
|
+
attrs['href'] = '#';
|
|
512
|
+
// Se for link externo, adicionar segurança
|
|
513
|
+
// attrs['rel'] = 'noopener noreferrer';
|
|
514
|
+
// attrs['target'] = '_blank';
|
|
515
|
+
}
|
|
516
|
+
// Inputs
|
|
517
|
+
if (tag === 'input') {
|
|
518
|
+
// Detecta tipo de input
|
|
519
|
+
if (normalizedName.includes('email')) {
|
|
520
|
+
attrs['type'] = 'email';
|
|
521
|
+
attrs['placeholder'] = 'email@example.com';
|
|
522
|
+
}
|
|
523
|
+
else if (normalizedName.includes('password') || normalizedName.includes('senha')) {
|
|
524
|
+
attrs['type'] = 'password';
|
|
525
|
+
attrs['placeholder'] = '********';
|
|
526
|
+
}
|
|
527
|
+
else if (normalizedName.includes('search') || normalizedName.includes('busca')) {
|
|
528
|
+
attrs['type'] = 'search';
|
|
529
|
+
attrs['placeholder'] = 'Search...';
|
|
530
|
+
}
|
|
531
|
+
else if (normalizedName.includes('phone') || normalizedName.includes('telefone') || normalizedName.includes('tel')) {
|
|
532
|
+
attrs['type'] = 'tel';
|
|
533
|
+
attrs['placeholder'] = '+1 (555) 000-0000';
|
|
534
|
+
}
|
|
535
|
+
else if (normalizedName.includes('number') || normalizedName.includes('numero') || normalizedName.includes('quantidade')) {
|
|
536
|
+
attrs['type'] = 'number';
|
|
537
|
+
}
|
|
538
|
+
else if (normalizedName.includes('date') || normalizedName.includes('data')) {
|
|
539
|
+
attrs['type'] = 'date';
|
|
540
|
+
}
|
|
541
|
+
else if (normalizedName.includes('time') || normalizedName.includes('hora')) {
|
|
542
|
+
attrs['type'] = 'time';
|
|
543
|
+
}
|
|
544
|
+
else if (normalizedName.includes('checkbox')) {
|
|
545
|
+
attrs['type'] = 'checkbox';
|
|
546
|
+
}
|
|
547
|
+
else if (normalizedName.includes('radio')) {
|
|
548
|
+
attrs['type'] = 'radio';
|
|
549
|
+
}
|
|
550
|
+
else if (normalizedName.includes('file') || normalizedName.includes('arquivo') || normalizedName.includes('upload')) {
|
|
551
|
+
attrs['type'] = 'file';
|
|
552
|
+
}
|
|
553
|
+
else if (normalizedName.includes('url') || normalizedName.includes('website') || normalizedName.includes('site')) {
|
|
554
|
+
attrs['type'] = 'url';
|
|
555
|
+
attrs['placeholder'] = 'https://';
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
attrs['type'] = 'text';
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// Textarea
|
|
562
|
+
if (tag === 'textarea') {
|
|
563
|
+
attrs['rows'] = '4';
|
|
564
|
+
}
|
|
565
|
+
// Select
|
|
566
|
+
if (tag === 'select') {
|
|
567
|
+
// Será preenchido com options
|
|
568
|
+
}
|
|
569
|
+
// Botões
|
|
570
|
+
if (tag === 'button') {
|
|
571
|
+
if (normalizedName.includes('submit') || normalizedName.includes('enviar') || normalizedName.includes('login')) {
|
|
572
|
+
attrs['type'] = 'submit';
|
|
573
|
+
}
|
|
574
|
+
else if (normalizedName.includes('reset') || normalizedName.includes('limpar')) {
|
|
575
|
+
attrs['type'] = 'reset';
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
attrs['type'] = 'button';
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// Imagens
|
|
582
|
+
if (tag === 'img') {
|
|
583
|
+
attrs['src'] = '';
|
|
584
|
+
attrs['alt'] = normalizedName.replace(/-/g, ' ');
|
|
585
|
+
attrs['loading'] = 'lazy';
|
|
586
|
+
}
|
|
587
|
+
// Vídeo
|
|
588
|
+
if (tag === 'video') {
|
|
589
|
+
attrs['controls'] = '';
|
|
590
|
+
}
|
|
591
|
+
// Áudio
|
|
592
|
+
if (tag === 'audio') {
|
|
593
|
+
attrs['controls'] = '';
|
|
594
|
+
}
|
|
595
|
+
// Dialog
|
|
596
|
+
if (tag === 'dialog') {
|
|
597
|
+
// dialog não precisa de atributos especiais por padrão
|
|
598
|
+
}
|
|
599
|
+
// Time
|
|
600
|
+
if (tag === 'time') {
|
|
601
|
+
attrs['datetime'] = '';
|
|
602
|
+
}
|
|
603
|
+
// Progress
|
|
604
|
+
if (tag === 'progress') {
|
|
605
|
+
attrs['value'] = '0';
|
|
606
|
+
attrs['max'] = '100';
|
|
607
|
+
}
|
|
608
|
+
// Meter
|
|
609
|
+
if (tag === 'meter') {
|
|
610
|
+
attrs['value'] = '0';
|
|
611
|
+
attrs['min'] = '0';
|
|
612
|
+
attrs['max'] = '100';
|
|
613
|
+
}
|
|
614
|
+
// Divider/hr não precisa de atributos
|
|
615
|
+
// =====================================================
|
|
616
|
+
// ARIA e Acessibilidade (a11y)
|
|
617
|
+
// =====================================================
|
|
618
|
+
// Alert role para notificações
|
|
619
|
+
if (SEMANTIC_PATTERNS.alert.test(normalizedName)) {
|
|
620
|
+
attrs['role'] = 'alert';
|
|
621
|
+
attrs['aria-live'] = 'polite';
|
|
622
|
+
}
|
|
623
|
+
// Navegação
|
|
624
|
+
if (tag === 'nav') {
|
|
625
|
+
// Pode adicionar aria-label se necessário
|
|
626
|
+
if (normalizedName.includes('main') || normalizedName.includes('principal')) {
|
|
627
|
+
attrs['aria-label'] = 'Main navigation';
|
|
628
|
+
}
|
|
629
|
+
else if (normalizedName.includes('breadcrumb')) {
|
|
630
|
+
attrs['aria-label'] = 'Breadcrumb';
|
|
631
|
+
}
|
|
632
|
+
else if (normalizedName.includes('footer')) {
|
|
633
|
+
attrs['aria-label'] = 'Footer navigation';
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
// Dialog/Modal
|
|
637
|
+
if (tag === 'dialog') {
|
|
638
|
+
attrs['aria-modal'] = 'true';
|
|
639
|
+
attrs['aria-labelledby'] = ''; // Deve ser preenchido com ID do título
|
|
640
|
+
}
|
|
641
|
+
// Accordion/Details
|
|
642
|
+
if (tag === 'details') {
|
|
643
|
+
// details/summary já tem semântica built-in
|
|
644
|
+
}
|
|
645
|
+
// Progress/Loading
|
|
646
|
+
if (SEMANTIC_PATTERNS.progress.test(normalizedName)) {
|
|
647
|
+
attrs['role'] = 'status';
|
|
648
|
+
attrs['aria-busy'] = 'true';
|
|
649
|
+
attrs['aria-label'] = 'Loading';
|
|
650
|
+
}
|
|
651
|
+
// Botões de ícone (sem texto visível)
|
|
652
|
+
if (tag === 'button' && SEMANTIC_PATTERNS.icon.test(normalizedName)) {
|
|
653
|
+
attrs['aria-label'] = normalizedName.replace(/-/g, ' ');
|
|
654
|
+
}
|
|
655
|
+
// Imagens decorativas vs informativas
|
|
656
|
+
if (tag === 'img') {
|
|
657
|
+
// Se for ícone ou decorativo, alt pode ser vazio
|
|
658
|
+
if (SEMANTIC_PATTERNS.icon.test(normalizedName) || normalizedName.includes('decorative')) {
|
|
659
|
+
attrs['alt'] = '';
|
|
660
|
+
attrs['aria-hidden'] = 'true';
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return attrs;
|
|
664
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptui-lib/figma-parser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
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.
|
|
33
|
+
"@promptui-lib/core": "0.1.8"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^20.0.0",
|