@promptui-lib/codegen 0.1.0 → 0.1.3

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.
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Flutter Template
3
+ * Generates Flutter/Dart widgets from Figma designs
4
+ */
5
+ /**
6
+ * Mapping of tokens to Flutter values
7
+ */
8
+ const TOKEN_TO_FLUTTER = {
9
+ // Colors
10
+ '$color-primary': 'Theme.of(context).primaryColor',
11
+ '$color-secondary': 'Theme.of(context).colorScheme.secondary',
12
+ '$color-success': 'Colors.green',
13
+ '$color-error': 'Colors.red',
14
+ '$color-warning': 'Colors.orange',
15
+ '$color-bg-primary': 'Colors.white',
16
+ '$color-bg-secondary': 'Colors.grey[100]',
17
+ '$color-text-primary': 'Colors.black87',
18
+ '$color-text-secondary': 'Colors.black54',
19
+ '$color-text-inverse': 'Colors.white',
20
+ // Spacing
21
+ '$spacing-xs': '4',
22
+ '$spacing-sm': '8',
23
+ '$spacing-md': '16',
24
+ '$spacing-lg': '24',
25
+ '$spacing-xl': '32',
26
+ '$spacing-2xl': '48',
27
+ // Border radius
28
+ '$radius-none': '0',
29
+ '$radius-small': '4',
30
+ '$radius-medium': '8',
31
+ '$radius-large': '12',
32
+ '$radius-xl': '16',
33
+ '$radius-full': '999',
34
+ // Font sizes
35
+ '$font-size-xs': '10',
36
+ '$font-size-sm': '12',
37
+ '$font-size-md': '14',
38
+ '$font-size-lg': '16',
39
+ '$font-size-xl': '20',
40
+ '$font-size-2xl': '24',
41
+ '$font-size-h1': '32',
42
+ '$font-size-h2': '28',
43
+ // Font weights
44
+ '$font-weight-regular': 'FontWeight.w400',
45
+ '$font-weight-medium': 'FontWeight.w500',
46
+ '$font-weight-semibold': 'FontWeight.w600',
47
+ '$font-weight-bold': 'FontWeight.w700',
48
+ };
49
+ /**
50
+ * Converts CSS value to Flutter
51
+ */
52
+ function cssToFlutter(property, value) {
53
+ // Token mapping
54
+ if (value.startsWith('$')) {
55
+ return TOKEN_TO_FLUTTER[value] ?? value;
56
+ }
57
+ // Parse numeric values
58
+ const numValue = parseFloat(value);
59
+ if (!isNaN(numValue)) {
60
+ return numValue.toString();
61
+ }
62
+ return value;
63
+ }
64
+ /**
65
+ * Generates EdgeInsets from padding/margin
66
+ */
67
+ function generateEdgeInsets(styles) {
68
+ const padding = {};
69
+ for (const style of styles) {
70
+ const value = cssToFlutter(style.property, style.token ?? style.value);
71
+ if (style.property === 'padding') {
72
+ return `EdgeInsets.all(${value})`;
73
+ }
74
+ if (style.property === 'padding-top')
75
+ padding.top = value;
76
+ if (style.property === 'padding-bottom')
77
+ padding.bottom = value;
78
+ if (style.property === 'padding-left')
79
+ padding.left = value;
80
+ if (style.property === 'padding-right')
81
+ padding.right = value;
82
+ }
83
+ if (Object.keys(padding).length > 0) {
84
+ const top = padding.top ?? '0';
85
+ const right = padding.right ?? '0';
86
+ const bottom = padding.bottom ?? '0';
87
+ const left = padding.left ?? '0';
88
+ if (top === bottom && left === right) {
89
+ if (top === left) {
90
+ return `EdgeInsets.all(${top})`;
91
+ }
92
+ return `EdgeInsets.symmetric(vertical: ${top}, horizontal: ${left})`;
93
+ }
94
+ return `EdgeInsets.only(top: ${top}, right: ${right}, bottom: ${bottom}, left: ${left})`;
95
+ }
96
+ return null;
97
+ }
98
+ /**
99
+ * Gets Flutter widget for element type
100
+ */
101
+ function getFlutterWidget(node) {
102
+ switch (node.tag) {
103
+ case 'button':
104
+ return 'ElevatedButton';
105
+ case 'img':
106
+ return 'Image.network';
107
+ case 'input':
108
+ return 'TextField';
109
+ case 'span':
110
+ case 'p':
111
+ case 'h1':
112
+ case 'h2':
113
+ case 'h3':
114
+ return 'Text';
115
+ default:
116
+ return 'Container';
117
+ }
118
+ }
119
+ /**
120
+ * Generates Flutter widget code
121
+ */
122
+ function generateFlutterWidget(node, styles, indent = 4) {
123
+ const spaces = ' '.repeat(indent);
124
+ const nodeStyles = styles.get(`.${node.className}`) ?? [];
125
+ // Determine widget type
126
+ const widget = getFlutterWidget(node);
127
+ // Build properties
128
+ const props = [];
129
+ // Padding
130
+ const padding = generateEdgeInsets(nodeStyles);
131
+ if (padding) {
132
+ props.push(`padding: ${padding}`);
133
+ }
134
+ // Background color
135
+ const bgColor = nodeStyles.find(s => s.property === 'background-color');
136
+ if (bgColor) {
137
+ const color = cssToFlutter('background-color', bgColor.token ?? bgColor.value);
138
+ props.push(`color: ${color}`);
139
+ }
140
+ // Border radius
141
+ const borderRadius = nodeStyles.find(s => s.property === 'border-radius');
142
+ if (borderRadius) {
143
+ const radius = cssToFlutter('border-radius', borderRadius.token ?? borderRadius.value);
144
+ props.push(`borderRadius: BorderRadius.circular(${radius})`);
145
+ }
146
+ // Children
147
+ const children = node.children.map(child => {
148
+ if (typeof child === 'string' || (typeof child === 'object' && 'type' in child && child.type === 'text')) {
149
+ const text = typeof child === 'string' ? child : child.value;
150
+ return `${spaces} Text('${text}')`;
151
+ }
152
+ return generateFlutterWidget(child, styles, indent + 2);
153
+ });
154
+ // Build widget
155
+ if (widget === 'Container') {
156
+ const decoration = [];
157
+ if (bgColor) {
158
+ decoration.push(`color: ${cssToFlutter('background-color', bgColor.token ?? bgColor.value)}`);
159
+ }
160
+ if (borderRadius) {
161
+ decoration.push(`borderRadius: BorderRadius.circular(${cssToFlutter('border-radius', borderRadius.token ?? borderRadius.value)})`);
162
+ }
163
+ const hasDecoration = decoration.length > 0;
164
+ const containerProps = [];
165
+ if (padding)
166
+ containerProps.push(`padding: ${padding}`);
167
+ if (hasDecoration) {
168
+ containerProps.push(`decoration: BoxDecoration(\n${spaces} ${decoration.join(',\n' + spaces + ' ')},\n${spaces} )`);
169
+ }
170
+ if (children.length === 1) {
171
+ containerProps.push(`child: ${children[0].trim()}`);
172
+ }
173
+ else if (children.length > 1) {
174
+ // Use Column or Row based on flex-direction
175
+ const flexDir = nodeStyles.find(s => s.property === 'flex-direction');
176
+ const layout = flexDir?.value === 'row' ? 'Row' : 'Column';
177
+ containerProps.push(`child: ${layout}(\n${spaces} children: [\n${children.join(',\n')},\n${spaces} ],\n${spaces} )`);
178
+ }
179
+ return `${spaces}Container(\n${spaces} ${containerProps.join(',\n' + spaces + ' ')},\n${spaces})`;
180
+ }
181
+ if (widget === 'Text') {
182
+ const text = node.children[0];
183
+ const textContent = typeof text === 'string' ? text : text?.value ?? '';
184
+ return `${spaces}Text('${textContent}')`;
185
+ }
186
+ if (widget === 'ElevatedButton') {
187
+ const buttonChild = children.length > 0 ? children[0].trim() : "Text('Button')";
188
+ return `${spaces}ElevatedButton(\n${spaces} onPressed: () {},\n${spaces} child: ${buttonChild},\n${spaces})`;
189
+ }
190
+ return `${spaces}Container()`;
191
+ }
192
+ /**
193
+ * Generates Flutter StatelessWidget
194
+ */
195
+ export function generateFlutterComponent(ast) {
196
+ // Create styles map
197
+ const stylesMap = new Map();
198
+ for (const block of ast.styles) {
199
+ stylesMap.set(block.selector, block.properties);
200
+ }
201
+ // Widget code
202
+ const widgetBody = generateFlutterWidget(ast.jsx, stylesMap);
203
+ return `/// ${ast.name}
204
+ /// Generated by PromptUI (Flutter)
205
+
206
+ import 'package:flutter/material.dart';
207
+
208
+ class ${ast.name} extends StatelessWidget {
209
+ const ${ast.name}({super.key});
210
+
211
+ @override
212
+ Widget build(BuildContext context) {
213
+ return ${widgetBody.trim()};
214
+ }
215
+ }
216
+ `;
217
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Framework Templates
3
- * Templates de geração para diferentes frameworks UI
3
+ * Templates for generating different UI frameworks
4
4
  */
5
- export type FrameworkType = 'react' | 'mui' | 'tailwind' | 'bootstrap';
5
+ export type FrameworkType = 'react' | 'mui' | 'tailwind' | 'bootstrap' | 'flutter' | 'swiftui';
6
6
  export interface IFrameworkConfig {
7
7
  name: FrameworkType;
8
8
  displayName: string;
@@ -16,4 +16,6 @@ export declare const FRAMEWORKS: Record<FrameworkType, IFrameworkConfig>;
16
16
  export { generateMuiComponent, generateMuiStyles } from './mui.template.js';
17
17
  export { generateTailwindComponent, generateTailwindClasses } from './tailwind.template.js';
18
18
  export { generateBootstrapComponent } from './bootstrap.template.js';
19
+ export { generateFlutterComponent } from './flutter.template.js';
20
+ export { generateSwiftUIComponent } from './swiftui.template.js';
19
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/frameworks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,CAAC;AAEvE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAqC9D,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/frameworks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;AAE/F,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAqD9D,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Framework Templates
3
- * Templates de geração para diferentes frameworks UI
3
+ * Templates for generating different UI frameworks
4
4
  */
5
5
  export const FRAMEWORKS = {
6
6
  react: {
@@ -39,7 +39,25 @@ export const FRAMEWORKS = {
39
39
  styleExtension: null, // Bootstrap classes
40
40
  imports: ["import type { ReactNode } from 'react';"],
41
41
  },
42
+ flutter: {
43
+ name: 'flutter',
44
+ displayName: 'Flutter',
45
+ description: 'Flutter/Dart StatelessWidget components',
46
+ fileExtension: 'dart',
47
+ styleExtension: null, // Inline styles
48
+ imports: ["import 'package:flutter/material.dart';"],
49
+ },
50
+ swiftui: {
51
+ name: 'swiftui',
52
+ displayName: 'SwiftUI',
53
+ description: 'SwiftUI View structs for iOS/macOS',
54
+ fileExtension: 'swift',
55
+ styleExtension: null, // Inline modifiers
56
+ imports: ['import SwiftUI'],
57
+ },
42
58
  };
43
59
  export { generateMuiComponent, generateMuiStyles } from './mui.template.js';
44
60
  export { generateTailwindComponent, generateTailwindClasses } from './tailwind.template.js';
45
61
  export { generateBootstrapComponent } from './bootstrap.template.js';
62
+ export { generateFlutterComponent } from './flutter.template.js';
63
+ export { generateSwiftUIComponent } from './swiftui.template.js';
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Material UI Template
3
- * Gera componentes React com MUI sx props
3
+ * Generates React components with MUI components and sx props
4
4
  */
5
5
  import type { IComponentAST } from '@promptui-lib/core';
6
6
  /**
7
- * Gera componente React com MUI
7
+ * Generates React component with MUI
8
8
  */
9
9
  export declare function generateMuiComponent(ast: IComponentAST): string;
10
10
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"mui.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/mui.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA4B,MAAM,oBAAoB,CAAC;AA6QlF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA+C/D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwB5D"}
1
+ {"version":3,"file":"mui.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/mui.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA4B,MAAM,oBAAoB,CAAC;AA2RlF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAgE/D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwB5D"}
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Material UI Template
3
- * Gera componentes React com MUI sx props
3
+ * Generates React components with MUI components and sx props
4
4
  */
5
+ import { getComponentMapping, extractComponentType } from '../mappings/index.js';
5
6
  /**
6
7
  * Mapeamento de tokens SCSS → MUI theme
7
8
  */
@@ -232,22 +233,47 @@ function generateMuiJSX(node, styles, indent = 2) {
232
233
  ].join('\n');
233
234
  }
234
235
  /**
235
- * Gera componente React com MUI
236
+ * Gets MUI component mapping based on component name
237
+ */
238
+ function getMuiMapping(componentName) {
239
+ const componentType = extractComponentType(componentName);
240
+ if (!componentType)
241
+ return null;
242
+ const mapping = getComponentMapping(componentType);
243
+ if (!mapping)
244
+ return null;
245
+ return mapping.mui;
246
+ }
247
+ /**
248
+ * Generates React component with MUI
236
249
  */
237
250
  export function generateMuiComponent(ast) {
238
- // Cria mapa de estilos por selector
251
+ // Create styles map by selector
239
252
  const stylesMap = new Map();
240
253
  for (const block of ast.styles) {
241
254
  stylesMap.set(block.selector, block.properties);
242
255
  }
256
+ // Check for component mapping
257
+ const mapping = getMuiMapping(ast.name);
243
258
  // Props interface
244
259
  const propsInterface = `export interface I${ast.name}Props {
245
260
  children?: ReactNode;
246
261
  className?: string;
247
262
  sx?: SxProps<Theme>;
248
263
  }`;
249
- // JSX
250
- const jsx = generateMuiJSX(ast.jsx, stylesMap);
264
+ // JSX - use mapping if available
265
+ let jsx;
266
+ if (mapping) {
267
+ const propsStr = mapping.props
268
+ ? Object.entries(mapping.props).map(([k, v]) => `${k}="${v}"`).join(' ')
269
+ : '';
270
+ jsx = ` <${mapping.component} ${propsStr}>
271
+ {children}
272
+ </${mapping.component}>`;
273
+ }
274
+ else {
275
+ jsx = generateMuiJSX(ast.jsx, stylesMap);
276
+ }
251
277
  // Component
252
278
  const component = `export const ${ast.name} = ({
253
279
  children,
@@ -258,12 +284,15 @@ export function generateMuiComponent(ast) {
258
284
  ${jsx}
259
285
  );
260
286
  };`;
261
- // Imports
262
- const imports = [
287
+ // Imports - include component-specific imports
288
+ const baseImports = [
263
289
  "import type { ReactNode } from 'react';",
264
290
  "import { Box, Stack, Typography, Button } from '@mui/material';",
265
291
  "import type { SxProps, Theme } from '@mui/material/styles';",
266
292
  ];
293
+ const imports = mapping?.imports
294
+ ? [...new Set([...baseImports, ...mapping.imports])]
295
+ : baseImports;
267
296
  return [
268
297
  '/**',
269
298
  ` * ${ast.name}`,
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SwiftUI Template
3
+ * Generates SwiftUI views from Figma designs
4
+ */
5
+ import type { IComponentAST } from '@promptui-lib/core';
6
+ /**
7
+ * Generates SwiftUI View struct
8
+ */
9
+ export declare function generateSwiftUIComponent(ast: IComponentAST): string;
10
+ //# sourceMappingURL=swiftui.template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swiftui.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/swiftui.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA4B,MAAM,oBAAoB,CAAC;AA0PlF;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAyBnE"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * SwiftUI Template
3
+ * Generates SwiftUI views from Figma designs
4
+ */
5
+ /**
6
+ * Mapping of tokens to SwiftUI values
7
+ */
8
+ const TOKEN_TO_SWIFTUI = {
9
+ // Colors
10
+ '$color-primary': '.blue',
11
+ '$color-secondary': '.gray',
12
+ '$color-success': '.green',
13
+ '$color-error': '.red',
14
+ '$color-warning': '.orange',
15
+ '$color-bg-primary': '.white',
16
+ '$color-bg-secondary': 'Color(.systemGray6)',
17
+ '$color-text-primary': '.primary',
18
+ '$color-text-secondary': '.secondary',
19
+ '$color-text-inverse': '.white',
20
+ // Spacing
21
+ '$spacing-xs': '4',
22
+ '$spacing-sm': '8',
23
+ '$spacing-md': '16',
24
+ '$spacing-lg': '24',
25
+ '$spacing-xl': '32',
26
+ '$spacing-2xl': '48',
27
+ // Border radius
28
+ '$radius-none': '0',
29
+ '$radius-small': '4',
30
+ '$radius-medium': '8',
31
+ '$radius-large': '12',
32
+ '$radius-xl': '16',
33
+ '$radius-full': '999',
34
+ // Font sizes
35
+ '$font-size-xs': '.caption2',
36
+ '$font-size-sm': '.caption',
37
+ '$font-size-md': '.body',
38
+ '$font-size-lg': '.headline',
39
+ '$font-size-xl': '.title2',
40
+ '$font-size-2xl': '.title',
41
+ '$font-size-h1': '.largeTitle',
42
+ '$font-size-h2': '.title',
43
+ // Font weights
44
+ '$font-weight-regular': '.regular',
45
+ '$font-weight-medium': '.medium',
46
+ '$font-weight-semibold': '.semibold',
47
+ '$font-weight-bold': '.bold',
48
+ };
49
+ /**
50
+ * Converts CSS value to SwiftUI
51
+ */
52
+ function cssToSwiftUI(property, value) {
53
+ // Token mapping
54
+ if (value.startsWith('$')) {
55
+ return TOKEN_TO_SWIFTUI[value] ?? value;
56
+ }
57
+ // Parse numeric values
58
+ const numValue = parseFloat(value);
59
+ if (!isNaN(numValue)) {
60
+ return numValue.toString();
61
+ }
62
+ // Color hex to SwiftUI
63
+ if (value.startsWith('#')) {
64
+ return `Color(hex: "${value}")`;
65
+ }
66
+ return value;
67
+ }
68
+ /**
69
+ * Gets SwiftUI view for element type
70
+ */
71
+ function getSwiftUIView(node) {
72
+ switch (node.tag) {
73
+ case 'button':
74
+ return 'Button';
75
+ case 'img':
76
+ return 'Image';
77
+ case 'input':
78
+ return 'TextField';
79
+ case 'span':
80
+ case 'p':
81
+ case 'h1':
82
+ case 'h2':
83
+ case 'h3':
84
+ return 'Text';
85
+ default:
86
+ return 'VStack'; // Default container
87
+ }
88
+ }
89
+ /**
90
+ * Generates SwiftUI modifiers from styles
91
+ */
92
+ function generateModifiers(styles, indent) {
93
+ const spaces = ' '.repeat(indent);
94
+ const modifiers = [];
95
+ for (const style of styles) {
96
+ const value = cssToSwiftUI(style.property, style.token ?? style.value);
97
+ switch (style.property) {
98
+ case 'padding':
99
+ modifiers.push(`${spaces}.padding(${value})`);
100
+ break;
101
+ case 'padding-top':
102
+ modifiers.push(`${spaces}.padding(.top, ${value})`);
103
+ break;
104
+ case 'padding-bottom':
105
+ modifiers.push(`${spaces}.padding(.bottom, ${value})`);
106
+ break;
107
+ case 'padding-left':
108
+ modifiers.push(`${spaces}.padding(.leading, ${value})`);
109
+ break;
110
+ case 'padding-right':
111
+ modifiers.push(`${spaces}.padding(.trailing, ${value})`);
112
+ break;
113
+ case 'background-color':
114
+ modifiers.push(`${spaces}.background(${value})`);
115
+ break;
116
+ case 'border-radius':
117
+ modifiers.push(`${spaces}.cornerRadius(${value})`);
118
+ break;
119
+ case 'color':
120
+ modifiers.push(`${spaces}.foregroundColor(${value})`);
121
+ break;
122
+ case 'font-size':
123
+ modifiers.push(`${spaces}.font(${value})`);
124
+ break;
125
+ case 'font-weight':
126
+ modifiers.push(`${spaces}.fontWeight(${value})`);
127
+ break;
128
+ case 'width':
129
+ if (value !== 'auto') {
130
+ modifiers.push(`${spaces}.frame(width: ${value})`);
131
+ }
132
+ break;
133
+ case 'height':
134
+ if (value !== 'auto') {
135
+ modifiers.push(`${spaces}.frame(height: ${value})`);
136
+ }
137
+ break;
138
+ }
139
+ }
140
+ return modifiers;
141
+ }
142
+ /**
143
+ * Generates SwiftUI view code
144
+ */
145
+ function generateSwiftUIView(node, styles, indent = 8) {
146
+ const spaces = ' '.repeat(indent);
147
+ const nodeStyles = styles.get(`.${node.className}`) ?? [];
148
+ // Determine view type
149
+ const view = getSwiftUIView(node);
150
+ // Get flex direction for layout
151
+ const flexDir = nodeStyles.find(s => s.property === 'flex-direction');
152
+ const layout = flexDir?.value === 'row' ? 'HStack' : 'VStack';
153
+ // Get alignment
154
+ const alignItems = nodeStyles.find(s => s.property === 'align-items');
155
+ let alignment = '';
156
+ if (alignItems) {
157
+ switch (alignItems.value) {
158
+ case 'center':
159
+ alignment = ', alignment: .center';
160
+ break;
161
+ case 'flex-start':
162
+ alignment = flexDir?.value === 'row' ? ', alignment: .top' : ', alignment: .leading';
163
+ break;
164
+ case 'flex-end':
165
+ alignment = flexDir?.value === 'row' ? ', alignment: .bottom' : ', alignment: .trailing';
166
+ break;
167
+ }
168
+ }
169
+ // Get gap/spacing
170
+ const gap = nodeStyles.find(s => s.property === 'gap');
171
+ const spacing = gap ? cssToSwiftUI('gap', gap.token ?? gap.value) : '0';
172
+ // Generate modifiers
173
+ const modifiers = generateModifiers(nodeStyles, indent);
174
+ // Children
175
+ const children = node.children.map(child => {
176
+ if (typeof child === 'string' || (typeof child === 'object' && 'type' in child && child.type === 'text')) {
177
+ const text = typeof child === 'string' ? child : child.value;
178
+ return `${spaces} Text("${text}")`;
179
+ }
180
+ return generateSwiftUIView(child, styles, indent + 4);
181
+ });
182
+ // Build view
183
+ if (view === 'Text') {
184
+ const text = node.children[0];
185
+ const textContent = typeof text === 'string' ? text : text?.value ?? '';
186
+ const textView = `${spaces}Text("${textContent}")`;
187
+ if (modifiers.length > 0) {
188
+ return textView + '\n' + modifiers.join('\n');
189
+ }
190
+ return textView;
191
+ }
192
+ if (view === 'Button') {
193
+ const buttonLabel = children.length > 0 ? children[0].trim() : 'Text("Button")';
194
+ const buttonView = `${spaces}Button(action: {}) {\n${spaces} ${buttonLabel}\n${spaces}}`;
195
+ if (modifiers.length > 0) {
196
+ return buttonView + '\n' + modifiers.join('\n');
197
+ }
198
+ return buttonView;
199
+ }
200
+ if (view === 'Image') {
201
+ return `${spaces}Image(systemName: "photo")\n${spaces} .resizable()\n${spaces} .aspectRatio(contentMode: .fit)`;
202
+ }
203
+ // Container (VStack/HStack)
204
+ if (children.length === 0) {
205
+ const emptyView = `${spaces}${layout}(spacing: ${spacing}${alignment}) {}`;
206
+ if (modifiers.length > 0) {
207
+ return emptyView + '\n' + modifiers.join('\n');
208
+ }
209
+ return emptyView;
210
+ }
211
+ const containerView = [
212
+ `${spaces}${layout}(spacing: ${spacing}${alignment}) {`,
213
+ ...children,
214
+ `${spaces}}`,
215
+ ].join('\n');
216
+ if (modifiers.length > 0) {
217
+ return containerView + '\n' + modifiers.join('\n');
218
+ }
219
+ return containerView;
220
+ }
221
+ /**
222
+ * Generates SwiftUI View struct
223
+ */
224
+ export function generateSwiftUIComponent(ast) {
225
+ // Create styles map
226
+ const stylesMap = new Map();
227
+ for (const block of ast.styles) {
228
+ stylesMap.set(block.selector, block.properties);
229
+ }
230
+ // View body
231
+ const viewBody = generateSwiftUIView(ast.jsx, stylesMap);
232
+ return `/// ${ast.name}
233
+ /// Generated by PromptUI (SwiftUI)
234
+
235
+ import SwiftUI
236
+
237
+ struct ${ast.name}: View {
238
+ var body: some View {
239
+ ${viewBody}
240
+ }
241
+ }
242
+
243
+ #Preview {
244
+ ${ast.name}()
245
+ }
246
+ `;
247
+ }
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@
5
5
  export * from './generators/index.js';
6
6
  export * from './writers/index.js';
7
7
  export * from './frameworks/index.js';
8
+ export * from './mappings/index.js';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -5,3 +5,4 @@
5
5
  export * from './generators/index.js';
6
6
  export * from './writers/index.js';
7
7
  export * from './frameworks/index.js';
8
+ export * from './mappings/index.js';